--- a/.hgignore Thu Nov 19 13:30:34 2015 +0300
+++ b/.hgignore Sun Dec 06 20:36:21 2015 +0300
@@ -55,7 +55,6 @@
glob:project_files/Android-build/out
glob:project_files/Android-build/Makefile.android
glob:hedgewars-build-desktop-Qt*
-glob:hedgewars-build-desktop-Qt*
glob:*.depends
glob:tools/build_windows_koda.bat
glob:share/hedgewars/Data/misc/hwengine.desktop
@@ -65,6 +64,9 @@
glob:*.tar.*
glob:*.or
glob:*.res
+glob:build-*
+glob:hedgewars-build-*
+glob:*.pro.user
glob:Hedgewars.app/*
glob:tools/CreateMacBundle.cmake
glob:share/Info.plist
@@ -80,3 +82,4 @@
glob:xcuserdata
glob:*.mode1v3
glob:*.mode2v3
+
--- a/CMakeLists.txt Thu Nov 19 13:30:34 2015 +0300
+++ b/CMakeLists.txt Sun Dec 06 20:36:21 2015 +0300
@@ -33,7 +33,7 @@
option(LUA_SYSTEM "Use system lua (on)" ON)
endif()
-option(BUILD_ENGINE_LIBRARY "Enable hwengine library (off)" OFF)
+set(BUILD_ENGINE_LIBRARY ON)
option(ANDROID "Enable Android build (off)" OFF)
option(MINIMAL_FLAGS "Respect system flags as much as possible (off)" OFF)
@@ -233,7 +233,7 @@
add_subdirectory(project_files/Android-build)
else(ANDROID)
add_subdirectory(bin)
- add_subdirectory(QTfrontend)
+ add_subdirectory(qmlFrontend)
add_subdirectory(share)
add_subdirectory(tools)
endif(ANDROID)
--- a/QTfrontend/net/tcpBase.cpp Thu Nov 19 13:30:34 2015 +0300
+++ b/QTfrontend/net/tcpBase.cpp Sun Dec 06 20:36:21 2015 +0300
@@ -111,8 +111,6 @@
m_connected(false),
IPCSocket(0)
{
- process = 0;
-
if(!IPCServer)
{
IPCServer = new QTcpServer(0);
--- a/hedgewars/ArgParsers.pas Thu Nov 19 13:30:34 2015 +0300
+++ b/hedgewars/ArgParsers.pas Sun Dec 06 20:36:21 2015 +0300
@@ -23,10 +23,15 @@
procedure GetParams;
{$IFDEF HWLIBRARY}
+{$IFDEF WIN32} // FIXME: what is the correct condition?
+var operatingsystem_parameter_argc: LongInt; external;
+ operatingsystem_parameter_argv: pointer; external;
+ operatingsystem_parameter_envp: pointer; external;
+{$ELSE}
var operatingsystem_parameter_argc: LongInt = 0; export;
operatingsystem_parameter_argv: pointer = nil; export;
operatingsystem_parameter_envp: pointer = nil; export;
-
+{$ENDIF}
function ParamCount: LongInt;
function ParamStr(i: LongInt): shortstring;
{$ENDIF}
@@ -121,17 +126,6 @@
SetVolume(0);
end;
-procedure setIpcPort(port: LongInt; var wrongParameter:Boolean);
-begin
- if isInternal then
- ipcPort := port
- else
- begin
- WriteLn(stderr, 'ERROR: use of --port is not allowed');
- wrongParameter := true;
- end
-end;
-
function parseNick(nick: shortstring): shortstring;
begin
if isInternal then
@@ -215,12 +209,12 @@
otherarray: array [0..2] of string = ('--locale','--fullscreen','--showfps');
mediaarray: array [0..9] of string = ('--fullscreen-width', '--fullscreen-height', '--width', '--height', '--depth', '--volume','--nomusic','--nosound','--locale','--fullscreen');
allarray: array [0..17] of string = ('--fullscreen-width','--fullscreen-height', '--width', '--height', '--depth','--volume','--nomusic','--nosound','--locale','--fullscreen','--showfps','--altdmg','--frame-interval','--low-quality','--no-teamtag','--no-hogtag','--no-healthtag','--translucent-tags');
- reallyAll: array[0..35] of shortstring = (
- '--prefix', '--user-prefix', '--locale', '--fullscreen-width', '--fullscreen-height', '--width',
+ reallyAll: array[0..32] of shortstring = (
+ '--locale', '--fullscreen-width', '--fullscreen-height', '--width',
'--height', '--frame-interval', '--volume','--nomusic', '--nosound',
'--fullscreen', '--showfps', '--altdmg', '--low-quality', '--raw-quality', '--stereo', '--nick',
{deprecated} '--depth', '--set-video', '--set-audio', '--set-other', '--set-multimedia', '--set-everything',
- {internal} '--internal', '--port', '--recorder', '--landpreview',
+ {internal} '--internal', '--recorder', '--landpreview',
{misc} '--stats-only', '--gci', '--help','--no-teamtag','--no-hogtag','--no-healthtag','--translucent-tags','--lua-test');
var cmdIndex: byte;
begin
@@ -232,45 +226,42 @@
while (cmdIndex <= High(reallyAll)) and (cmd <> reallyAll[cmdIndex]) do inc(cmdIndex);
case cmdIndex of
- {--prefix} 0 : PathPrefix := getstringParameter (arg, paramIndex, parseParameter);
- {--user-prefix} 1 : UserPathPrefix := getstringParameter (arg, paramIndex, parseParameter);
- {--locale} 2 : cLocaleFName := getstringParameter (arg, paramIndex, parseParameter);
- {--fullscreen-width} 3 : cFullscreenWidth := max(getLongIntParameter(arg, paramIndex, parseParameter), cMinScreenWidth);
- {--fullscreen-height} 4 : cFullscreenHeight := max(getLongIntParameter(arg, paramIndex, parseParameter), cMinScreenHeight);
- {--width} 5 : cWindowedWidth := max(2 * (getLongIntParameter(arg, paramIndex, parseParameter) div 2), cMinScreenWidth);
- {--height} 6 : cWindowedHeight := max(2 * (getLongIntParameter(arg, paramIndex, parseParameter) div 2), cMinScreenHeight);
- {--frame-interval} 7 : cTimerInterval := getLongIntParameter(arg, paramIndex, parseParameter);
- {--volume} 8 : SetVolume ( max(getLongIntParameter(arg, paramIndex, parseParameter), 0) );
- {--nomusic} 9 : SetMusic ( false );
- {--nosound} 10 : SetSound ( false );
- {--fullscreen} 11 : cFullScreen := true;
- {--showfps} 12 : cShowFPS := true;
- {--altdmg} 13 : cAltDamage := true;
- {--low-quality} 14 : cReducedQuality := $FFFFFFFF xor rqLowRes;
- {--raw-quality} 15 : cReducedQuality := getLongIntParameter(arg, paramIndex, parseParameter);
- {--stereo} 16 : setStereoMode ( getLongIntParameter(arg, paramIndex, parseParameter) );
- {--nick} 17 : UserNick := parseNick( getstringParameter(arg, paramIndex, parseParameter) );
+ {--locale} 0 : cLocaleFName := getstringParameter (arg, paramIndex, parseParameter);
+ {--fullscreen-width} 1 : cFullscreenWidth := max(getLongIntParameter(arg, paramIndex, parseParameter), cMinScreenWidth);
+ {--fullscreen-height} 2 : cFullscreenHeight := max(getLongIntParameter(arg, paramIndex, parseParameter), cMinScreenHeight);
+ {--width} 3 : cWindowedWidth := max(2 * (getLongIntParameter(arg, paramIndex, parseParameter) div 2), cMinScreenWidth);
+ {--height} 4 : cWindowedHeight := max(2 * (getLongIntParameter(arg, paramIndex, parseParameter) div 2), cMinScreenHeight);
+ {--frame-interval} 5 : cTimerInterval := getLongIntParameter(arg, paramIndex, parseParameter);
+ {--volume} 6 : SetVolume ( max(getLongIntParameter(arg, paramIndex, parseParameter), 0) );
+ {--nomusic} 7 : SetMusic ( false );
+ {--nosound} 8 : SetSound ( false );
+ {--fullscreen} 9 : cFullScreen := true;
+ {--showfps} 10 : cShowFPS := true;
+ {--altdmg} 11 : cAltDamage := true;
+ {--low-quality} 12 : cReducedQuality := $FFFFFFFF xor rqLowRes;
+ {--raw-quality} 13 : cReducedQuality := getLongIntParameter(arg, paramIndex, parseParameter);
+ {--stereo} 14 : setStereoMode ( getLongIntParameter(arg, paramIndex, parseParameter) );
+ {--nick} 15 : UserNick := parseNick( getstringParameter(arg, paramIndex, parseParameter) );
{deprecated options}
- {--depth} 18 : setDepth(paramIndex);
- {--set-video} 19 : parseClassicParameter(videoarray,5,paramIndex);
- {--set-audio} 20 : parseClassicParameter(audioarray,3,paramIndex);
- {--set-other} 21 : parseClassicParameter(otherarray,3,paramIndex);
- {--set-multimedia} 22 : parseClassicParameter(mediaarray,10,paramIndex);
- {--set-everything} 23 : parseClassicParameter(allarray,14,paramIndex);
+ {--depth} 16 : setDepth(paramIndex);
+ {--set-video} 17 : parseClassicParameter(videoarray,5,paramIndex);
+ {--set-audio} 18 : parseClassicParameter(audioarray,3,paramIndex);
+ {--set-other} 19 : parseClassicParameter(otherarray,3,paramIndex);
+ {--set-multimedia} 20 : parseClassicParameter(mediaarray,10,paramIndex);
+ {--set-everything} 21 : parseClassicParameter(allarray,14,paramIndex);
{"internal" options}
- {--internal} 24 : {$IFDEF HWLIBRARY}isInternal:= true{$ENDIF};
- {--port} 25 : setIpcPort( getLongIntParameter(arg, paramIndex, parseParameter), parseParameter );
- {--recorder} 26 : startVideoRecording(paramIndex);
- {--landpreview} 27 : GameType := gmtLandPreview;
+ {--internal} 22 : {$IFDEF HWLIBRARY}isInternal:= true{$ENDIF};
+ {--recorder} 23 : startVideoRecording(paramIndex);
+ {--landpreview} 24 : GameType := gmtLandPreview;
{anything else}
- {--stats-only} 28 : statsOnlyGame();
- {--gci} 29 : GciEasterEgg();
- {--help} 30 : DisplayUsage();
- {--no-teamtag} 31 : cTagsMask := cTagsMask and (not htTeamName);
- {--no-hogtag} 32 : cTagsMask := cTagsMask and (not htName);
- {--no-healthtag} 33 : cTagsMask := cTagsMask and (not htHealth);
- {--translucent-tags} 34 : cTagsMask := cTagsMask or htTransparent;
- {--lua-test} 35 : begin cTestLua := true; SetSound(false); cScriptName := getstringParameter(arg, paramIndex, parseParameter); WriteLn(stdout, 'Lua test file specified: ' + cScriptName);end;
+ {--stats-only} 25 : statsOnlyGame();
+ {--gci} 26 : GciEasterEgg();
+ {--help} 27 : DisplayUsage();
+ {--no-teamtag} 28 : cTagsMask := cTagsMask and (not htTeamName);
+ {--no-hogtag} 29 : cTagsMask := cTagsMask and (not htName);
+ {--no-healthtag} 30 : cTagsMask := cTagsMask and (not htHealth);
+ {--translucent-tags} 31 : cTagsMask := cTagsMask or htTransparent;
+ {--lua-test} 32: begin cTestLua := true; SetSound(false); cScriptName := getstringParameter(arg, paramIndex, parseParameter); WriteLn(stdout, 'Lua test file specified: ' + cScriptName);end;
else
begin
//Assume the first "non parameter" is the replay file, anything else is invalid
@@ -332,12 +323,12 @@
paramTotal: LongInt;
index, nextIndex: LongInt;
wrongParameter: boolean;
-//var tmpInt: LongInt;
+var tmpInt: LongInt;
begin
paramIndex:= {$IFDEF HWLIBRARY}0{$ELSE}1{$ENDIF};
paramTotal:= ParamCount; //-1 because pascal enumeration is inclusive
- (*
+
WriteLn(stdout, 'total parameters: ' + inttostr(paramTotal));
tmpInt:= 0;
while (tmpInt <= paramTotal) do
@@ -345,7 +336,7 @@
WriteLn(stdout, inttostr(tmpInt) + ': ' + {$IFDEF HWLIBRARY}argv[tmpInt]{$ELSE}paramCount(tmpInt){$ENDIF});
inc(tmpInt);
end;
- *)
+
wrongParameter:= false;
while (paramIndex <= paramTotal) do
begin
@@ -362,26 +353,29 @@
procedure GetParams;
begin
- isInternal:= (ParamStr(1) = '--internal');
-
- UserPathPrefix := _S'.';
- PathPrefix := cDefaultPathPrefix;
- recordFileName := '';
- parseCommandLine();
-
- if (isInternal) and (ParamCount<=1) then
+ if ParamCount > 0 then
begin
- WriteLn(stderr, '--internal should not be manually used');
- GameType := gmtSyntax;
- end;
+ isInternal:= (ParamStr(1) = '--internal');
+
+ recordFileName := '';
+ parseCommandLine();
+
+ if (isInternal) and (ParamCount<=1) then
+ begin
+ WriteLn(stderr, '--internal should not be manually used');
+ GameType := gmtSyntax;
+ end;
- if (not cTestLua) and (not isInternal) and (recordFileName = '') then
- begin
- WriteLn(stderr, 'You must specify a replay file');
- GameType := gmtSyntax;
- end
- else if (recordFileName <> '') then
- WriteLn(stdout, 'Attempting to play demo file "' + recordFilename + '"');
+ if (not cTestLua) and (not isInternal) and (recordFileName = '') then
+ begin
+ WriteLn(stderr, 'You must specify a replay file');
+ GameType := gmtSyntax;
+ end
+ else if (recordFileName <> '') then
+ WriteLn(stdout, 'Attempting to play demo file "' + recordFilename + '"');
+ end
+ else
+ GameType:= gmtSyntax;
if (GameType = gmtSyntax) then
WriteLn(stderr, 'Please use --help to see possible arguments and their usage');
--- a/hedgewars/CMakeLists.txt Thu Nov 19 13:30:34 2015 +0300
+++ b/hedgewars/CMakeLists.txt Sun Dec 06 20:36:21 2015 +0300
@@ -106,6 +106,21 @@
uGearsUtils.pas
uTeams.pas
+ uFLAmmo.pas
+ uFLGameConfig.pas
+ uFLIPC.pas
+ uFLNet.pas
+ uFLNetProtocol.pas
+ uFLNetTypes.pas
+ uFLRunQueue.pas
+ uFLScripts.pas
+ uFLSchemes.pas
+ uFLTeams.pas
+ uFLThemes.pas
+ uFLTypes.pas
+ uFLUICallback.pas
+ uFLUtils.pas
+
#these interact with everything, so compile last
uScript.pas
)
--- a/hedgewars/SDLh.pas Thu Nov 19 13:30:34 2015 +0300
+++ b/hedgewars/SDLh.pas Sun Dec 06 20:36:21 2015 +0300
@@ -910,6 +910,8 @@
PSDL_Thread = Pointer;
PSDL_mutex = Pointer;
+ PSDL_sem = Pointer;
+ PSDL_cond = Pointer;
TSDL_GLattr = (
SDL_GL_RED_SIZE,
@@ -1117,13 +1119,30 @@
(or have fun debugging nil arguments) *)
function SDL_CreateThread(fn: Pointer; name: PChar; data: Pointer): PSDL_Thread; cdecl; external SDLLibName;
procedure SDL_WaitThread(thread: PSDL_Thread; status: PLongInt); cdecl; external SDLLibName;
-procedure SDL_KillThread(thread: PSDL_Thread); cdecl; external SDLLibName;
+procedure SDL_DetachThread(thread: PSDL_Thread); cdecl; external SDLLibName;
function SDL_CreateMutex: PSDL_mutex; cdecl; external SDLLibName;
procedure SDL_DestroyMutex(mutex: PSDL_mutex); cdecl; external SDLLibName;
function SDL_LockMutex(mutex: PSDL_mutex): LongInt; cdecl; external SDLLibName;
function SDL_UnlockMutex(mutex: PSDL_mutex): LongInt; cdecl; external SDLLibName;
+function SDL_CreateCond: PSDL_cond; cdecl; external SDLLibName;
+procedure SDL_DestroyCond(cond: PSDL_cond); cdecl; external SDLLibName;
+function SDL_CondSignal(cond: PSDL_cond): LongInt; cdecl; external SDLLibName;
+function SDL_CondBroadcast(cond: PSDL_cond): LongInt; cdecl; external SDLLibName;
+function SDL_CondWait(cond: PSDL_cond; mut: PSDL_mutex): LongInt; cdecl; external SDLLibName;
+function SDL_CondWaitTimeout(cond: PSDL_cond; mut: PSDL_mutex; ms: Longword): LongInt; cdecl; external SDLLibName;
+
+
+function SDL_CreateSemaphore(initial_value: Longword): PSDL_sem; cdecl; external SDLLibName;
+procedure SDL_DestroySemaphore(sem: PSDL_sem); cdecl; external SDLLibName;
+function SDL_SemWait(sem: PSDL_sem): LongInt; cdecl; external SDLLibName;
+function SDL_SemTryWait(sem: PSDL_sem): LongInt; cdecl; external SDLLibName;
+function SDL_SemWaitTimeout(sem: PSDL_sem; ms: Longword): LongInt; cdecl; external SDLLibName;
+function SDL_SemPost(sem: PSDL_sem): LongInt; cdecl; external SDLLibName;
+function SDL_SemValue(sem: PSDL_sem): Longword; cdecl; external SDLLibName;
+
+
function SDL_GL_SetAttribute(attr: TSDL_GLattr; value: LongInt): LongInt; cdecl; external SDLLibName;
procedure SDL_GL_SwapBuffers; cdecl; external SDLLibName;
--- a/hedgewars/hwLibrary.pas Thu Nov 19 13:30:34 2015 +0300
+++ b/hedgewars/hwLibrary.pas Sun Dec 06 20:36:21 2015 +0300
@@ -29,65 +29,100 @@
Library hwLibrary;
-uses hwengine, uTypes, uConsts, uVariables, uSound, uCommands, uUtils,
- uLocale{$IFDEF ANDROID}, jni{$ENDIF};
+uses hwengine
+ , uTypes
+ , uConsts
+ , uVariables
+ , uSound
+ , uCommands
+ , uUtils
+ , uLocale
+ {$IFDEF ANDROID}, jni{$ENDIF}
+ , uFLTypes
+ , uFLGameConfig
+ , uFLIPC
+ , uPhysFSLayer
+ , uFLThemes
+ , uFLTeams
+ , uFLScripts
+ , uFLSchemes
+ , uFLAmmo
+ , uFLNet
+ , uFLNetProtocol
+ , uFLUICallback
+ , uFLRunQueue
+ ;
{$INCLUDE "config.inc"}
// retrieve protocol information
-procedure HW_versionInfo(netProto: PLongInt; versionStr: PPChar); cdecl; export;
+procedure HW_versionInfo(netProto: PLongInt; versionStr: PPChar); cdecl;
begin
netProto^:= cNetProtoVersion;
versionStr^:= cVersionString;
end;
-function HW_versionString: PChar; cdecl; export;
+function HW_versionString: PChar; cdecl;
begin
exit(cVersionString + '-r' + cRevisionString + ' (' + cHashString + ')');
end;
// equivalent to esc+y; when closeFrontend = true the game exits after memory cleanup
-procedure HW_terminate(closeFrontend: boolean); cdecl; export;
+procedure HW_terminate(closeFrontend: boolean); cdecl;
begin
closeFrontend:= closeFrontend; // avoid hint
ParseCommand('forcequit', true);
end;
-function HW_getWeaponNameByIndex(whichone: LongInt): PChar; cdecl; export;
+function HW_getWeaponNameByIndex(whichone: LongInt): PChar; cdecl;
begin
HW_getWeaponNameByIndex:= (str2pchar(trammo[Ammoz[TAmmoType(whichone+1)].NameId]));
end;
-(*function HW_getWeaponCaptionByIndex(whichone: LongInt): PChar; cdecl; export;
+(*function HW_getWeaponCaptionByIndex(whichone: LongInt): PChar; cdecl;
begin
HW_getWeaponCaptionByIndex:= (str2pchar(trammoc[Ammoz[TAmmoType(whichone+1)].NameId]));
end;
-function HW_getWeaponDescriptionByIndex(whichone: LongInt): PChar; cdecl; export;
+function HW_getWeaponDescriptionByIndex(whichone: LongInt): PChar; cdecl;
begin
HW_getWeaponDescriptionByIndex:= (str2pchar(trammod[Ammoz[TAmmoType(whichone+1)].NameId]));
end;*)
-function HW_getNumberOfWeapons: LongInt; cdecl; export;
+function HW_getNumberOfWeapons: LongInt; cdecl;
begin
HW_getNumberOfWeapons:= ord(high(TAmmoType));
end;
-function HW_getMaxNumberOfHogs: LongInt; cdecl; export;
+function HW_getMaxNumberOfHogs: LongInt; cdecl;
begin
HW_getMaxNumberOfHogs:= cMaxHHIndex + 1;
end;
-function HW_getMaxNumberOfTeams: LongInt; cdecl; export;
+function HW_getMaxNumberOfTeams: LongInt; cdecl;
begin
HW_getMaxNumberOfTeams:= cMaxTeams;
end;
-procedure HW_memoryWarningCallback; cdecl; export;
+procedure HW_memoryWarningCallback; cdecl;
begin
ReleaseSound(false);
end;
+procedure flibInit(localPrefix, userPrefix: PChar); cdecl;
+begin
+ initIPC;
+ uPhysFSLayer.initModule(localPrefix, userPrefix);
+ uFLNet.initModule;
+end;
+
+procedure flibFree; cdecl;
+begin
+ uFLNet.freeModule;
+ uPhysFSLayer.freemodule;
+ freeIPC;
+end;
+
{$IFDEF ANDROID}
function JNI_HW_versionInfoNet(env: PJNIEnv; obj: JObject):JInt;cdecl;
begin
@@ -119,6 +154,39 @@
Game;
{$ELSE}
exports
+ runQuickGame,
+ runLocalGame,
+ getPreview,
+ registerUIMessagesCallback,
+ flibInit,
+ flibFree,
+ //game config
+ resetGameConfig,
+ setSeed,
+ getSeed,
+ setTheme,
+ setScript,
+ setScheme,
+ setAmmo,
+ getThemesList,
+ freeThemesList,
+ getThemeIcon,
+ getScriptsList,
+ getSchemesList,
+ getAmmosList,
+ getTeamsList,
+ tryAddTeam,
+ tryRemoveTeam,
+ changeTeamColor,
+ // network
+ connectOfficialServer,
+ passNetData,
+ passFlibEvent,
+ sendChatLine,
+ joinRoom,
+ partRoom,
+
+ // dunno what these are
RunEngine,
LoadLocaleWrapper,
HW_versionInfo,
--- a/hedgewars/hwengine.pas Thu Nov 19 13:30:34 2015 +0300
+++ b/hedgewars/hwengine.pas Sun Dec 06 20:36:21 2015 +0300
@@ -22,12 +22,8 @@
{$R res/hwengine.rc}
{$ENDIF}
-{$IFDEF HWLIBRARY}
unit hwengine;
interface
-{$ELSE}
-program hwengine;
-{$ENDIF}
uses {$IFDEF IPHONEOS}cmem, {$ENDIF} SDLh, uMisc, uConsole, uGame, uConsts, uLand, uAmmos, uVisualGears, uGears, uStore, uWorld, uInputHandler
, uSound, uScript, uTeams, uStats, uIO, uLocale, uChat, uAI, uAIMisc, uAILandMarks, uLandTexture, uCollisions
@@ -38,19 +34,13 @@
{$IFDEF ANDROID}, GLUnit{$ENDIF}
;
-{$IFDEF HWLIBRARY}
-procedure RunEngine(argc: LongInt; argv: PPChar); cdecl; export;
-
+function RunEngine(argc: LongInt; argv: PPChar): Longint; cdecl; export;
procedure preInitEverything();
procedure initEverything(complete:boolean);
procedure freeEverything(complete:boolean);
implementation
-{$ELSE}
-procedure preInitEverything(); forward;
-procedure initEverything(complete:boolean); forward;
-procedure freeEverything(complete:boolean); forward;
-{$ENDIF}
+uses uFLUICallback, uFLTypes;
///////////////////////////////////////////////////////////////////////////////
function DoTimer(Lag: LongInt): boolean;
@@ -314,8 +304,8 @@
initEverything(true);
WriteLnToConsole('Hedgewars engine ' + cVersionString + '-r' + cRevisionString +
' (' + cHashString + ') with protocol #' + inttostr(cNetProtoVersion));
- AddFileLog('Prefix: "' + shortstring(PathPrefix) +'"');
- AddFileLog('UserPrefix: "' + shortstring(UserPathPrefix) +'"');
+ //AddFileLog('Prefix: "' + shortstring(PathPrefix) +'"');
+ //AddFileLog('UserPrefix: "' + shortstring(UserPathPrefix) +'"');
for i:= 0 to ParamCount do
AddFileLog(inttostr(i) + ': ' + ParamStr(i));
@@ -373,7 +363,6 @@
begin
if recordFileName = '' then
begin
- InitIPC;
SendIPCAndWaitReply(_S'C'); // ask for game config
end
else
@@ -432,8 +421,8 @@
uLand.initModule; // computes land
uLandPainted.initModule; // computes drawn land
uIO.initModule; // sets up sockets
- uPhysFSLayer.initModule;
uScript.initModule;
+ uTeams.initModule; // clear CurrentTeam variable
if complete then
begin
@@ -457,7 +446,6 @@
uStats.initModule;
uStore.initModule;
uRender.initModule;
- uTeams.initModule;
uVisualGears.initModule;
uVisualGearsHandlers.initModule;
uWorld.initModule;
@@ -501,7 +489,6 @@
uCommands.freeModule;
uVariables.freeModule;
uUtils.freeModule; // closes debug file
- uPhysFSLayer.freeModule;
uScript.freeModule;
end;
@@ -515,7 +502,6 @@
begin
initEverything(false);
- InitIPC;
IPCWaitPongEvent;
TryDo(InitStepsFlags = cifRandomize, 'Some parameters not set (flags = ' + inttostr(InitStepsFlags) + ')', true);
@@ -532,18 +518,25 @@
freeEverything(false);
end;
-{$IFDEF HWLIBRARY}
-procedure RunEngine(argc: LongInt; argv: PPChar); cdecl; export;
+function EngineThread(p: pointer): Longint; cdecl; export;
+var e: TFLIBEvent;
+begin
+ if GameType = gmtLandPreview then
+ GenLandPreview()
+ else Game();
+
+ e:= flibGameFinished;
+ sendUI(mtFlibEvent, @e, sizeof(e));
+ EngineThread:= 0
+end;
+
+
+function RunEngine(argc: LongInt; argv: PPChar): Longint; cdecl; export;
+var t: PSDL_Thread;
begin
operatingsystem_parameter_argc:= argc;
operatingsystem_parameter_argv:= argv;
-{$ELSE}
-begin
-{$ENDIF}
-///////////////////////////////////////////////////////////////////////////////
-/////////////////////////////////// m a i n ///////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////
{$IFDEF PAS2C}
// workaround for pascal's ParamStr and ParamCount
init(argc, argv);
@@ -552,40 +545,14 @@
GetParams();
- if GameType = gmtLandPreview then
- GenLandPreview()
- else if GameType <> gmtSyntax then
- Game();
-
- // return 1 when engine is not called correctly
if GameType = gmtSyntax then
- {$IFDEF PAS2C}
- exit(HaltUsageError);
- {$ELSE}
- halt(HaltUsageError);
- {$ENDIF}
-
- if cTestLua then
- begin
- WriteLnToConsole(errmsgLuaTestTerm);
- {$IFDEF PAS2C}
- exit(HaltTestUnexpected);
- {$ELSE}
- halt(HaltTestUnexpected);
- {$ENDIF}
- end;
-
- {$IFDEF PAS2C}
- exit(HaltNoError);
- {$ELSE}
- {$IFDEF IPHONEOS}
- exit;
- {$ELSE}
- halt(HaltNoError);
- {$ENDIF}
- {$ENDIF}
-{$IFDEF HWLIBRARY}
+ RunEngine:= HaltUsageError
+ else
+ begin
+ t:= SDL_CreateThread(@EngineThread, 'engine', nil);
+ SDL_DetachThread(t);
+ RunEngine:= 0
+ end
end;
-{$ENDIF}
end.
--- a/hedgewars/uDebug.pas Thu Nov 19 13:30:34 2015 +0300
+++ b/hedgewars/uDebug.pas Sun Dec 06 20:36:21 2015 +0300
@@ -33,12 +33,7 @@
begin
WriteLnToConsole(Msg);
if isFatalError then
- begin
ParseCommand('fatal ' + lastConsoleline, true);
- // hint for the 'coverity' source analyzer
- // this halt is never actually reached because ParseCommands will halt first
- halt(HaltFatalError);
- end;
end;
procedure TryDo(Assert: boolean; Msg: shortstring; isFatal: boolean);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hedgewars/uFLAmmo.pas Sun Dec 06 20:36:21 2015 +0300
@@ -0,0 +1,136 @@
+unit uFLAmmo;
+interface
+uses uFLTypes;
+
+function getAmmosList: PPChar; cdecl;
+procedure freeAmmosList;
+
+function ammoByName(s: shortstring): PAmmo;
+procedure sendAmmoConfig(var ammo: TAmmo);
+
+implementation
+uses uFLUtils, uFLIPC, uPhysFSLayer, uFLData;
+
+const MAX_AMMO_NAMES = 64;
+type
+ TAmmoArray = array [0..0] of TAmmo;
+ PAmmoArray = ^TAmmoArray;
+var
+ ammoList: PAmmo;
+ ammoNumber: LongInt;
+ listOfAmmoNames: array[0..MAX_AMMO_NAMES] of PChar;
+
+procedure loadAmmo;
+var f: PFSFile;
+ ammo: PAmmo;
+ ammos: PAmmoArray;
+ s: ansistring;
+ i: Longword;
+begin
+ f:= pfsOpenRead('/Config/weapons.ini');
+ ammoNumber:= 0;
+
+ if f <> nil then
+ begin
+ while (not pfsEOF(f)) do
+ begin
+ pfsReadLnA(f, s);
+
+ if (length(s) > 0) and (s[1] <> '[') then
+ inc(ammoNumber);
+ end;
+
+ //inc(ammoNumber); // add some default ammo
+
+ ammoList:= GetMem(sizeof(ammoList^) * (ammoNumber + 1));
+ ammo:= PAmmo(ammoList);
+ pfsSeek(f, 0);
+
+ while (not pfsEOF(f)) do
+ begin
+ pfsReadLnA(f, s);
+
+ i:= 1;
+ while(i <= length(s)) and (s[i] <> '=') do inc(i);
+
+ if i < length(s) then
+ begin
+ ammo^.ammoName:= copy(s, 1, i - 1);
+ delete(s, 1, i);
+ // TODO: split into 4 shortstrings
+ i:= length(s) div 4;
+ ammo^.a:= copy(s, 1, i);
+ ammo^.b:= copy(s, i + 1, i);
+ ammo^.c:= copy(s, i * 2 + 1, i);
+ ammo^.d:= copy(s, i * 3 + 1, i);
+ inc(ammo)
+ end;
+ end;
+
+ pfsClose(f)
+ end;
+end;
+
+
+function getAmmosList: PPChar; cdecl;
+var i, t, l: Longword;
+ ammo: PAmmo;
+begin
+ if ammoList = nil then
+ loadAmmo;
+
+ t:= ammoNumber;
+ if t >= MAX_AMMO_NAMES then
+ t:= MAX_AMMO_NAMES;
+
+ ammo:= ammoList;
+ for i:= 0 to Pred(t) do
+ begin
+ l:= length(ammo^.ammoName);
+ if l >= 255 then l:= 254;
+ ammo^.ammoName[l + 1]:= #0;
+ listOfAmmoNames[i]:= @ammo^.ammoName[1];
+ inc(ammo)
+ end;
+
+ listOfAmmoNames[t]:= nil;
+
+ getAmmosList:= listOfAmmoNames
+end;
+
+function ammoByName(s: shortstring): PAmmo;
+var i: Longword;
+ ammo: PAmmo;
+begin
+ ammo:= ammoList;
+ i:= 0;
+ while (i < ammoNumber) and (ammo^.ammoName <> s) do
+ begin
+ inc(ammo);
+ inc(i)
+ end;
+
+ if i < ammoNumber then ammoByName:= ammo else ammoByName:= nil
+end;
+
+procedure freeAmmosList;
+begin
+ if ammoList <> nil then
+ FreeMem(ammoList, sizeof(ammoList^) * (ammoNumber + 1))
+end;
+
+
+procedure sendAmmoConfig(var ammo: TAmmo);
+var i: Longword;
+begin
+ with ammo do
+ begin
+ ipcToEngine('eammloadt ' + ammo.a);
+ ipcToEngine('eammprob ' + ammo.b);
+ ipcToEngine('eammdelay ' + ammo.c);
+ ipcToEngine('eammreinf ' + ammo.d);
+ ipcToEngine('eammstore');
+ end
+end;
+
+end.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hedgewars/uFLGameConfig.pas Sun Dec 06 20:36:21 2015 +0300
@@ -0,0 +1,625 @@
+unit uFLGameConfig;
+interface
+uses uFLTypes;
+
+procedure resetGameConfig; cdecl;
+procedure runQuickGame; cdecl;
+procedure runLocalGame; cdecl;
+procedure getPreview; cdecl;
+
+procedure setSeed(seed: PChar); cdecl;
+function getSeed: PChar; cdecl;
+procedure setTheme(themeName: PChar); cdecl;
+procedure setScript(scriptName: PChar); cdecl;
+procedure setScheme(schemeName: PChar); cdecl;
+procedure setAmmo(ammoName: PChar); cdecl;
+
+procedure tryAddTeam(teamName: PChar); cdecl;
+procedure tryRemoveTeam(teamName: PChar); cdecl;
+procedure changeTeamColor(teamName: PChar; dir: LongInt); cdecl;
+
+procedure netSetSeed(seed: shortstring);
+procedure netSetTheme(themeName: shortstring);
+procedure netSetScript(scriptName: shortstring);
+procedure netSetFeatureSize(fsize: LongInt);
+procedure netSetMapGen(mapgen: LongInt);
+procedure netSetMap(map: shortstring);
+procedure netSetMazeSize(mazesize: LongInt);
+procedure netSetTemplate(template: LongInt);
+procedure netSetAmmo(name: shortstring; definition: ansistring);
+procedure netSetScheme(scheme: TScheme);
+procedure netAddTeam(team: TTeam);
+procedure netAcceptedTeam(teamName: shortstring);
+procedure netSetTeamColor(team: shortstring; color: Longword);
+procedure netSetHedgehogsNumber(team: shortstring; hogsNumber: Longword);
+procedure netRemoveTeam(teamName: shortstring);
+procedure netResetTeams();
+procedure updatePreviewIfNeeded;
+
+procedure sendConfig(config: PGameConfig);
+
+implementation
+uses uFLIPC, uFLUtils, uFLTeams, uFLThemes, uFLSChemes, uFLAmmo, uFLUICallback, uFLRunQueue, uFLNet;
+
+var
+ currentConfig: TGameConfig;
+ previewNeedsUpdate: boolean;
+
+function getScriptPath(scriptName: shortstring): shortstring;
+begin
+ getScriptPath:= '/Scripts/Multiplayer/' + scriptName + '.lua'
+end;
+
+procedure sendConfig(config: PGameConfig);
+var i: Longword;
+begin
+with config^ do
+begin
+ case gameType of
+ gtPreview: begin
+ if script <> 'Normal' then
+ ipcToEngine('escript ' + getScriptPath(script));
+ ipcToEngine('eseed ' + seed);
+ ipcToEngine('e$mapgen ' + intToStr(mapgen));
+ if (mapgen = 1) or (mapgen = 2) then
+ ipcToEngine('e$maze_size ' + intToStr(mazeSize));
+ else
+ ipcToEngine('e$template_filter ' + intToStr(template));
+ ipcToEngine('e$feature_size ' + intToStr(featureSize));
+ end;
+ gtLocal: begin
+ if script <> 'Normal' then
+ ipcToEngine('escript ' + getScriptPath(script));
+ ipcToEngine('eseed ' + seed);
+ ipcToEngine('e$mapgen ' + intToStr(mapgen));
+ if (mapgen = 1) or (mapgen = 2) then
+ ipcToEngine('e$maze_size ' + intToStr(mazeSize));
+ else
+ ipcToEngine('e$template_filter ' + intToStr(template));
+ ipcToEngine('e$feature_size ' + intToStr(featureSize));
+ ipcToEngine('e$theme ' + theme);
+
+ sendSchemeConfig(scheme);
+
+ i:= 0;
+ while (i < 8) and (teams[i].hogsNumber > 0) do
+ begin
+ sendTeamConfig(teams[i]);
+ sendAmmoConfig(config^.ammo);
+ inc(i)
+ end;
+ end;
+ end;
+
+ ipcToEngine('!');
+end;
+end;
+
+procedure resetGameConfig; cdecl;
+var i: Longword;
+begin
+ with currentConfig do
+ begin
+ script:= 'Normal';
+
+ for i:= 0 to 7 do
+ teams[i].hogsNumber:= 0
+ end
+end;
+
+procedure setSeed(seed: PChar); cdecl;
+begin
+ sendUI(mtSeed, @seed[1], length(seed));
+ currentConfig.seed:= seed
+end;
+
+function getSeed: PChar; cdecl;
+begin
+ getSeed:= str2PChar(currentConfig.seed)
+end;
+
+function getUnusedColor: Longword;
+var i, c: Longword;
+ fColorMatched: boolean;
+begin
+ c:= 0;
+ i:= 0;
+ repeat
+ repeat
+ fColorMatched:= (currentConfig.teams[i].hogsNumber > 0) and (currentConfig.teams[i].color = c);
+ inc(i)
+ until (i >= 8) or (currentConfig.teams[i].hogsNumber = 0) or fColorMatched;
+
+ if fColorMatched then
+ begin
+ i:= 0;
+ inc(c)
+ end;
+ until not fColorMatched;
+
+ getUnusedColor:= c
+end;
+
+procedure runQuickGame; cdecl;
+begin
+ with currentConfig do
+ begin
+ gameType:= gtLocal;
+ arguments[0]:= '';
+ arguments[1]:= '--internal';
+ arguments[2]:= '--nomusic';
+ argumentsNumber:= 3;
+
+ teams[0]:= createRandomTeam;
+ teams[0].color:= 0;
+ teams[1]:= createRandomTeam;
+ teams[1].color:= 1;
+ teams[1].botLevel:= 3;
+
+ queueExecution(currentConfig);
+ end;
+end;
+
+
+procedure getPreview; cdecl;
+begin
+ previewNeedsUpdate:= false;
+
+ with currentConfig do
+ begin
+ gameType:= gtPreview;
+ arguments[0]:= '';
+ arguments[1]:= '--internal';
+ arguments[2]:= '--landpreview';
+ argumentsNumber:= 3;
+
+ queueExecution(currentConfig);
+ end;
+end;
+
+procedure runLocalGame; cdecl;
+begin
+ with currentConfig do
+ begin
+ gameType:= gtLocal;
+ arguments[0]:= '';
+ arguments[1]:= '--internal';
+ arguments[2]:= '--nomusic';
+ argumentsNumber:= 3;
+
+ queueExecution(currentConfig);
+ end;
+end;
+
+procedure tryAddTeam(teamName: PChar); cdecl;
+var msg: ansistring;
+ i, hn, hedgehogsNumber: Longword;
+ team: PTeam;
+ c: Longword;
+begin
+ team:= teamByName(teamName);
+ if team = nil then exit;
+
+ if isConnected then
+ sendTeam(team^)
+ else
+ with currentConfig do
+ begin
+ hedgehogsNumber:= 0;
+ i:= 0;
+
+ while (i < 8) and (teams[i].hogsNumber > 0) do
+ begin
+ inc(i);
+ inc(hedgehogsNumber, teams[i].hogsNumber)
+ end;
+
+ // no free space for a team or reached hogs number maximum
+ if (i > 7) or (hedgehogsNumber >= 48) then exit;
+
+ c:= getUnusedColor;
+
+ teams[i]:= team^;
+
+ if i = 0 then hn:= 4 else hn:= teams[i - 1].hogsNumber;
+ if hn > 48 - hedgehogsNumber then hn:= 48 - hedgehogsNumber;
+ teams[i].hogsNumber:= hn;
+
+ teams[i].color:= c;
+
+ msg:= '0' + #10 + teamName;
+ sendUI(mtAddPlayingTeam, @msg[1], length(msg));
+
+ msg:= teamName + #10 + colorsSet[teams[i].color];
+ sendUI(mtTeamColor, @msg[1], length(msg));
+
+ msg:= teamName + #10 + IntToStr(hn);
+ sendUI(mtHedgehogsNumber, @msg[1], length(msg));
+
+ msg:= teamName;
+ sendUI(mtRemoveTeam, @msg[1], length(msg))
+ end
+end;
+
+
+procedure tryRemoveTeam(teamName: PChar); cdecl;
+var i: Longword;
+ tn: shortstring;
+ isLocal: boolean;
+begin
+ with currentConfig do
+ begin
+ i:= 0;
+ tn:= teamName;
+ while (i < 8) and (teams[i].teamName <> tn) do
+ inc(i);
+
+ // team not found???
+ if (i > 7) then exit;
+
+ isLocal:= not teams[i].extDriven;
+
+ if isConnected and not isLocal then
+ exit; // we cannot remove this team
+
+ while (i < 7) and (teams[i + 1].hogsNumber > 0) do
+ begin
+ teams[i]:= teams[i + 1];
+ inc(i)
+ end;
+
+ teams[i].hogsNumber:= 0
+ end;
+
+ sendUI(mtRemovePlayingTeam, @tn[1], length(tn));
+ if isConnected then
+ removeTeam(tn);
+ if isLocal then
+ sendUI(mtAddTeam, @tn[1], length(tn))
+end;
+
+
+procedure changeTeamColor(teamName: PChar; dir: LongInt); cdecl;
+var i, dc: Longword;
+ tn: shortstring;
+ msg: ansistring;
+begin
+ with currentConfig do
+ begin
+ i:= 0;
+ tn:= teamName;
+ while (i < 8) and (teams[i].teamName <> tn) do
+ inc(i);
+ // team not found???
+ if (i > 7) then exit;
+
+ if dir >= 0 then dc:= 1 else dc:= 8;
+ teams[i].color:= (teams[i].color + dc) mod 9;
+
+ msg:= tn + #10 + colorsSet[teams[i].color];
+ sendUI(mtTeamColor, @msg[1], length(msg))
+ end
+end;
+
+procedure setTheme(themeName: PChar); cdecl;
+begin
+ currentConfig.theme:= themeName
+end;
+
+procedure setScript(scriptName: PChar); cdecl;
+begin
+ currentConfig.script:= scriptName
+end;
+
+procedure setScheme(schemeName: PChar); cdecl;
+var scheme: PScheme;
+begin
+ scheme:= schemeByName(schemeName);
+
+ if scheme <> nil then
+ currentConfig.scheme:= scheme^
+end;
+
+procedure setAmmo(ammoName: PChar); cdecl;
+var ammo: PAmmo;
+begin
+ ammo:= ammoByName(ammoName);
+
+ if ammo <> nil then
+ currentConfig.ammo:= ammo^
+end;
+
+procedure netSetSeed(seed: shortstring);
+begin
+ if seed <> currentConfig.seed then
+ begin
+ currentConfig.seed:= seed;
+ sendUI(mtSeed, @seed[1], length(seed));
+
+ getPreview()
+ end
+end;
+
+procedure netSetTheme(themeName: shortstring);
+begin
+ if themeName <> currentConfig.theme then
+ begin
+ currentConfig.theme:= themeName;
+ sendUI(mtTheme, @themeName[1], length(themeName))
+ end
+end;
+
+procedure netSetScript(scriptName: shortstring);
+begin
+ if scriptName <> currentConfig.script then
+ begin
+ previewNeedsUpdate:= true;
+ currentConfig.script:= scriptName;
+ sendUI(mtScript, @scriptName[1], length(scriptName))
+ end
+end;
+
+procedure netSetFeatureSize(fsize: LongInt);
+var s: shortstring;
+begin
+ if fsize <> currentConfig.featureSize then
+ begin
+ previewNeedsUpdate:= true;
+ currentConfig.featureSize:= fsize;
+ s:= IntToStr(fsize);
+ sendUI(mtFeatureSize, @s[1], length(s))
+ end
+end;
+
+procedure netSetMapGen(mapgen: LongInt);
+var s: shortstring;
+begin
+ if mapgen <> currentConfig.mapgen then
+ begin
+ previewNeedsUpdate:= true;
+ currentConfig.mapgen:= mapgen;
+ s:= IntToStr(mapgen);
+ sendUI(mtMapGen, @s[1], length(s))
+ end
+end;
+
+procedure netSetMap(map: shortstring);
+begin
+ sendUI(mtMap, @map[1], length(map))
+end;
+
+procedure netSetMazeSize(mazesize: LongInt);
+var s: shortstring;
+begin
+ if mazesize <> currentConfig.mazesize then
+ begin
+ previewNeedsUpdate:= true;
+ currentConfig.mazesize:= mazesize;
+ s:= IntToStr(mazesize);
+ sendUI(mtMazeSize, @s[1], length(s))
+ end
+end;
+
+procedure netSetTemplate(template: LongInt);
+var s: shortstring;
+begin
+ if template <> currentConfig.template then
+ begin
+ previewNeedsUpdate:= true;
+ currentConfig.template:= template;
+ s:= IntToStr(template);
+ sendUI(mtTemplate, @s[1], length(s))
+ end
+end;
+
+procedure updatePreviewIfNeeded;
+begin
+ if previewNeedsUpdate then
+ getPreview
+end;
+
+procedure netSetAmmo(name: shortstring; definition: ansistring);
+var ammo: TAmmo;
+ i: LongInt;
+begin
+ ammo.ammoName:= name;
+ i:= length(definition) div 4;
+ ammo.a:= copy(definition, 1, i);
+ ammo.b:= copy(definition, i + 1, i);
+ ammo.c:= copy(definition, i * 2 + 1, i);
+ ammo.d:= copy(definition, i * 3 + 1, i);
+
+ currentConfig.ammo:= ammo;
+ sendUI(mtAmmo, @name[1], length(name))
+end;
+
+procedure netSetScheme(scheme: TScheme);
+begin
+ currentConfig.scheme:= scheme;
+ sendUI(mtScheme, @scheme.schemeName[1], length(scheme.schemeName))
+end;
+
+procedure netAddTeam(team: TTeam);
+var msg: ansistring;
+ i, hn, hedgehogsNumber: Longword;
+ c: Longword;
+begin
+ with currentConfig do
+ begin
+ hedgehogsNumber:= 0;
+ i:= 0;
+
+ while (i < 8) and (teams[i].hogsNumber > 0) do
+ begin
+ inc(i);
+ inc(hedgehogsNumber, teams[i].hogsNumber)
+ end;
+
+ // no free space for a team - server bug???
+ if (i > 7) or (hedgehogsNumber >= 48) then exit;
+
+ c:= getUnusedColor;
+
+ teams[i]:= team;
+ teams[i].extDriven:= true;
+
+ if i = 0 then hn:= 4 else hn:= teams[i - 1].hogsNumber;
+ if hn > 48 - hedgehogsNumber then hn:= 48 - hedgehogsNumber;
+ teams[i].hogsNumber:= hn;
+
+ teams[i].color:= c;
+
+ msg:= '0' + #10 + team.teamName;
+ sendUI(mtAddPlayingTeam, @msg[1], length(msg));
+
+ msg:= team.teamName + #10 + colorsSet[teams[i].color];
+ sendUI(mtTeamColor, @msg[1], length(msg));
+ end
+end;
+
+procedure netAcceptedTeam(teamName: shortstring);
+var msg: ansistring;
+ i, hn, hedgehogsNumber: Longword;
+ c: Longword;
+ team: PTeam;
+begin
+ with currentConfig do
+ begin
+ team:= teamByName(teamName);
+ // no such team???
+ if team = nil then exit;
+
+ hedgehogsNumber:= 0;
+ i:= 0;
+
+ while (i < 8) and (teams[i].hogsNumber > 0) do
+ begin
+ inc(i);
+ inc(hedgehogsNumber, teams[i].hogsNumber)
+ end;
+
+ // no free space for a team - server bug???
+ if (i > 7) or (hedgehogsNumber >= 48) then exit;
+
+ c:= getUnusedColor;
+
+ teams[i]:= team^;
+ teams[i].extDriven:= false;
+
+ if i = 0 then hn:= 4 else hn:= teams[i - 1].hogsNumber;
+ if hn > 48 - hedgehogsNumber then hn:= 48 - hedgehogsNumber;
+ teams[i].hogsNumber:= hn;
+
+ teams[i].color:= c;
+
+ msg:= '0' + #10 + teamName;
+ sendUI(mtAddPlayingTeam, @msg[1], length(msg));
+
+ msg:= teamName + #10 + colorsSet[teams[i].color];
+ sendUI(mtTeamColor, @msg[1], length(msg));
+
+ msg:= teamName;
+ sendUI(mtRemoveTeam, @msg[1], length(msg))
+ end
+end;
+
+procedure netRemoveTeam(teamName: shortstring);
+var msg: shortstring;
+ i: Longword;
+ tn: shortstring;
+ isLocal: boolean;
+begin
+ with currentConfig do
+ begin
+ i:= 0;
+ tn:= teamName;
+ while (i < 8) and (teams[i].teamName <> tn) do
+ inc(i);
+
+ // team not found???
+ if (i > 7) then exit;
+
+ isLocal:= not teams[i].extDriven;
+
+ while (i < 7) and (teams[i + 1].hogsNumber > 0) do
+ begin
+ teams[i]:= teams[i + 1];
+ inc(i)
+ end;
+
+ teams[i].hogsNumber:= 0
+ end;
+
+ msg:= teamName;
+
+ sendUI(mtRemovePlayingTeam, @msg[1], length(msg));
+ if isLocal then
+ sendUI(mtAddTeam, @msg[1], length(msg))
+end;
+
+procedure netSetTeamColor(team: shortstring; color: Longword);
+var i: Longword;
+ msg: ansistring;
+begin
+ with currentConfig do
+ begin
+ i:= 0;
+
+ while (i < 8) and (teams[i].teamName <> team) do
+ inc(i);
+ // team not found???
+ if (i > 7) then exit;
+
+ teams[i].color:= color mod 9;
+
+ msg:= team + #10 + colorsSet[teams[i].color];
+ sendUI(mtTeamColor, @msg[1], length(msg))
+ end
+end;
+
+procedure netSetHedgehogsNumber(team: shortstring; hogsNumber: Longword);
+var i: Longword;
+ msg: ansistring;
+begin
+ if hogsNumber > 8 then exit;
+
+ with currentConfig do
+ begin
+ i:= 0;
+
+ while (i < 8) and (teams[i].teamName <> team) do
+ inc(i);
+ // team not found???
+ if (i > 7) then exit;
+
+ teams[i].hogsNumber:= hogsNumber;
+
+ msg:= team + #10 + IntToStr(hogsNumber);
+ sendUI(mtHedgehogsNumber, @msg[1], length(msg))
+ end
+end;
+
+procedure netResetTeams();
+var msg: shortstring;
+ i: Longword;
+begin
+ with currentConfig do
+ begin
+ i:= 0;
+
+ while (i < 8) and (teams[i].hogsNumber > 0) do
+ begin
+ msg:= teams[i].teamName;
+
+ sendUI(mtRemovePlayingTeam, @msg[1], length(msg));
+ if not teams[i].extDriven then
+ sendUI(mtAddTeam, @msg[1], length(msg));
+
+ teams[i].hogsNumber:= 0;
+ inc(i)
+ end;
+
+ end;
+end;
+
+end.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hedgewars/uFLIPC.pas Sun Dec 06 20:36:21 2015 +0300
@@ -0,0 +1,238 @@
+unit uFLIPC;
+interface
+uses SDLh, uFLTypes;
+
+var msgFrontend, msgEngine, msgNet: TIPCMessage;
+ mutFrontend, mutEngine, mutNet: PSDL_mutex;
+ condFrontend, condEngine, condNet: PSDL_cond;
+
+procedure initIPC;
+procedure freeIPC;
+
+procedure ipcToEngine(s: shortstring);
+procedure ipcToEngineRaw(p: pointer; len: Longword);
+//function ipcReadFromEngine: shortstring;
+//function ipcCheckFromEngine: boolean;
+
+procedure ipcToNet(s: shortstring);
+procedure ipcToNetRaw(p: pointer; len: Longword);
+
+procedure ipcToFrontend(s: shortstring);
+procedure ipcToFrontendRaw(p: pointer; len: Longword);
+function ipcReadFromFrontend: shortstring;
+function ipcCheckFromFrontend: boolean;
+
+procedure registerIPCCallback(p: pointer; f: TIPCCallback);
+procedure registerNetCallback(p: pointer; f: TIPCCallback);
+
+implementation
+
+var callbackPointerF: pointer;
+ callbackFunctionF: TIPCCallback;
+ callbackListenerThreadF: PSDL_Thread;
+ callbackPointerN: pointer;
+ callbackFunctionN: TIPCCallback;
+ callbackListenerThreadN: PSDL_Thread;
+
+procedure ipcSend(var s: TIPCMessage; var msg: TIPCMessage; mut: PSDL_mutex; cond: PSDL_cond);
+begin
+ SDL_LockMutex(mut);
+
+ while (msg.str[0] > #0) or (msg.buf <> nil) do
+ SDL_CondWait(cond, mut);
+
+ msg:= s;
+ SDL_CondSignal(cond);
+ SDL_UnlockMutex(mut);
+end;
+
+function ipcRead(var msg: TIPCMessage; mut: PSDL_mutex; cond: PSDL_cond): TIPCMessage;
+var tmp: pointer;
+begin
+ SDL_LockMutex(mut);
+ while (msg.str[0] = #0) and (msg.buf = nil) do
+ SDL_CondWait(cond, mut);
+
+ if msg.buf <> nil then
+// FIXME is this copying really needed, the buffer is in another thread already anyway?
+ begin
+ tmp:= msg.buf;
+ msg.buf:= GetMem(msg.len);
+ Move(tmp^, msg.buf^, msg.len);
+ FreeMem(tmp, msg.len)
+ end;
+
+ ipcRead:= msg;
+
+ msg.str[0]:= #0;
+ msg.buf:= nil;
+
+ SDL_CondSignal(cond);
+ SDL_UnlockMutex(mut)
+end;
+
+function ipcCheck(var msg: TIPCMessage; mut: PSDL_mutex): boolean;
+begin
+ SDL_LockMutex(mut);
+ ipcCheck:= (msg.str[0] > #0) or (msg.buf <> nil);
+ SDL_UnlockMutex(mut)
+end;
+
+procedure ipcToEngine(s: shortstring);
+var msg: TIPCMessage;
+begin
+ msg.str:= s;
+ msg.buf:= nil;
+ ipcSend(msg, msgEngine, mutEngine, condEngine)
+end;
+
+procedure ipcToFrontend(s: shortstring);
+var msg: TIPCMessage;
+begin
+ msg.str:= s;
+ msg.buf:= nil;
+ ipcSend(msg, msgFrontend, mutFrontend, condFrontend)
+end;
+
+procedure ipcToNet(s: shortstring);
+var msg: TIPCMessage;
+begin
+ msg.str:= s;
+ msg.buf:= nil;
+ ipcSend(msg, msgNet, mutNet, condNet)
+end;
+
+procedure ipcToEngineRaw(p: pointer; len: Longword);
+var msg: TIPCMessage;
+begin
+ msg.str[0]:= #0;
+ msg.len:= len;
+ msg.buf:= GetMem(len);
+ Move(p^, msg.buf^, len);
+ ipcSend(msg, msgEngine, mutEngine, condEngine)
+end;
+
+procedure ipcToFrontendRaw(p: pointer; len: Longword);
+var msg: TIPCMessage;
+begin
+ msg.str[0]:= #0;
+ msg.len:= len;
+ msg.buf:= GetMem(len);
+ Move(p^, msg.buf^, len);
+ ipcSend(msg, msgFrontend, mutFrontend, condFrontend)
+end;
+
+procedure ipcToNetRaw(p: pointer; len: Longword);
+var msg: TIPCMessage;
+begin
+ msg.str[0]:= #0;
+ msg.len:= len;
+ msg.buf:= GetMem(len);
+ Move(p^, msg.buf^, len);
+ ipcSend(msg, msgNet, mutNet, condNet)
+end;
+
+function ipcReadFromEngine: TIPCMessage;
+begin
+ ipcReadFromEngine:= ipcRead(msgFrontend, mutFrontend, condFrontend)
+end;
+
+function ipcReadFromFrontend: shortstring;
+begin
+ ipcReadFromFrontend:= ipcRead(msgEngine, mutEngine, condEngine).str
+end;
+
+function ipcReadToNet: TIPCMessage;
+begin
+ ipcReadToNet:= ipcRead(msgNet, mutNet, condNet)
+end;
+
+function ipcCheckFromEngine: boolean;
+begin
+ ipcCheckFromEngine:= ipcCheck(msgFrontend, mutFrontend)
+end;
+
+function ipcCheckFromFrontend: boolean;
+begin
+ ipcCheckFromFrontend:= ipcCheck(msgEngine, mutEngine)
+end;
+
+function engineListener(p: pointer): Longint; cdecl; export;
+var msg: TIPCMessage;
+begin
+ engineListener:= 0;
+ repeat
+ msg:= ipcReadFromEngine();
+ if msg.buf = nil then
+ callbackFunctionF(callbackPointerF, @msg.str[1], byte(msg.str[0]))
+ else
+ begin
+ callbackFunctionF(callbackPointerF, msg.buf, msg.len);
+ FreeMem(msg.buf, msg.len)
+ end
+ until false
+end;
+
+function netListener(p: pointer): Longint; cdecl; export;
+var msg: TIPCMessage;
+begin
+ netListener:= 0;
+ repeat
+ msg:= ipcReadToNet();
+ if msg.buf = nil then
+ callbackFunctionN(callbackPointerN, @msg.str[1], byte(msg.str[0]))
+ else
+ begin
+ callbackFunctionN(callbackPointerN, msg.buf, msg.len);
+ FreeMem(msg.buf, msg.len)
+ end
+ until false
+end;
+
+procedure registerIPCCallback(p: pointer; f: TIPCCallback);
+begin
+ callbackPointerF:= p;
+ callbackFunctionF:= f;
+ callbackListenerThreadF:= SDL_CreateThread(@engineListener, 'engineListener', nil);
+end;
+
+procedure registerNetCallback(p: pointer; f: TIPCCallback);
+begin
+ callbackPointerN:= p;
+ callbackFunctionN:= f;
+ callbackListenerThreadN:= SDL_CreateThread(@netListener, 'netListener', nil);
+end;
+
+procedure initIPC;
+begin
+ msgFrontend.str:= '';
+ msgFrontend.buf:= nil;
+ msgEngine.str:= '';
+ msgEngine.buf:= nil;
+ msgNet.str:= '';
+ msgNet.buf:= nil;
+
+ callbackPointerF:= nil;
+ callbackListenerThreadF:= nil;
+
+ mutFrontend:= SDL_CreateMutex;
+ mutEngine:= SDL_CreateMutex;
+ mutNet:= SDL_CreateMutex;
+ condFrontend:= SDL_CreateCond;
+ condEngine:= SDL_CreateCond;
+ condNet:= SDL_CreateCond;
+end;
+
+procedure freeIPC;
+begin
+ //FIXME SDL_KillThread(callbackListenerThreadF);
+ //FIXME SDL_KillThread(callbackListenerThreadN);
+ SDL_DestroyMutex(mutFrontend);
+ SDL_DestroyMutex(mutEngine);
+ SDL_DestroyMutex(mutNet);
+ SDL_DestroyCond(condFrontend);
+ SDL_DestroyCond(condEngine);
+ SDL_DestroyCond(condNet);
+end;
+
+end.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hedgewars/uFLNet.pas Sun Dec 06 20:36:21 2015 +0300
@@ -0,0 +1,406 @@
+unit uFLNet;
+interface
+
+procedure connectOfficialServer;
+
+procedure initModule;
+procedure freeModule;
+procedure sendNet(s: shortstring);
+procedure sendNetLn(s: shortstring);
+
+var isConnected: boolean = false;
+
+implementation
+uses SDLh, uFLIPC, uFLTypes, uFLUICallback, uFLNetTypes, uFLUtils;
+
+const endCmd: shortstring = #10 + #10;
+
+function getNextChar: char; forward;
+function getCurrChar: char; forward;
+
+type
+ TParserState = record
+ cmd: TCmdType;
+ l: LongInt;
+ buf: shortstring;
+ bufpos: byte;
+ end;
+ PHandler = procedure;
+
+var state: TParserState;
+
+procedure handleTail; forward;
+function getShortString: shortstring; forward;
+function getLongString: ansistring; forward;
+
+procedure handler_;
+begin
+ sendUI(mtNetData, @state.cmd, sizeof(state.cmd));
+ handleTail()
+end;
+
+procedure handler_L;
+var cmd: TCmdParamL;
+begin
+ cmd.cmd:= state.cmd;
+ cmd.str1:= getLongString;
+ if length(cmd.str1) = 0 then exit;
+ sendUI(mtNetData, @cmd, sizeof(cmd));
+ handleTail()
+end;
+
+procedure handler_ML;
+var cmd: TCmdParamL;
+ f: boolean;
+begin
+ writeln('handler_ML');
+ sendUI(mtNetData, @state.cmd, sizeof(state.cmd));
+ cmd.cmd:= Succ(state.cmd);
+
+ repeat
+ cmd.str1:= getLongString;
+ f:= cmd.str1[0] <> #0;
+ if f then
+ sendUI(mtNetData, @cmd, sizeof(cmd));
+ until not f;
+ state.l:= 0
+end;
+
+procedure handler_MS;
+var cmd: TCmdParamS;
+ f: boolean;
+begin
+ sendUI(mtNetData, @state.cmd, sizeof(state.cmd));
+ cmd.cmd:= Succ(state.cmd);
+
+ repeat
+ cmd.str1:= getShortString;
+ f:= cmd.str1[0] <> #0;
+ if f then
+ sendUI(mtNetData, @cmd, sizeof(cmd));
+ until not f;
+ state.l:= 0
+end;
+
+procedure handler_S;
+var cmd: TCmdParamS;
+begin
+ cmd.cmd:= state.cmd;
+ cmd.str1:= getShortString;
+ if cmd.str1[0] = #0 then exit;
+ sendUI(mtNetData, @cmd, sizeof(cmd));
+ handleTail()
+end;
+
+procedure handler_SL;
+var cmd: TCmdParamSL;
+begin
+ cmd.cmd:= state.cmd;
+ cmd.str1:= getShortString;
+ if cmd.str1[0] = #0 then exit;
+ cmd.str2:= getLongString;
+ if cmd.str2[0] = #0 then exit;
+ sendUI(mtNetData, @cmd, sizeof(cmd));
+ handleTail()
+end;
+
+procedure handler_SMS;
+var cmd: TCmdParamS;
+ f: boolean;
+begin
+ cmd.cmd:= state.cmd;
+ cmd.str1:= getShortString;
+ if cmd.str1[0] = #0 then exit;
+ sendUI(mtNetData, @cmd, sizeof(cmd));
+
+ cmd.cmd:= Succ(state.cmd);
+ repeat
+ cmd.str1:= getShortString;
+ f:= cmd.str1[0] <> #0;
+ if f then
+ sendUI(mtNetData, @cmd, sizeof(cmd));
+ until not f;
+ state.l:= 0
+end;
+
+procedure handler_SS;
+var cmd: TCmdParamSS;
+begin
+ cmd.cmd:= state.cmd;
+ cmd.str1:= getShortString;
+ if cmd.str1[0] = #0 then exit;
+ cmd.str2:= getShortString;
+ if cmd.str2[0] = #0 then exit;
+ sendUI(mtNetData, @cmd, sizeof(cmd));
+ handleTail()
+end;
+
+procedure handler__i;
+var cmd: TCmdParami;
+ s: shortstring;
+begin
+ s:= getShortString();
+ if s[0] = #0 then exit;
+ cmd.cmd:= state.cmd;
+ s:= getShortString();
+ if s[0] = #0 then exit;
+ cmd.param1:= strToInt(s);
+ sendUI(mtNetData, @cmd, sizeof(cmd));
+ handleTail()
+end;
+
+procedure handler_i;
+var cmd: TCmdParami;
+ s: shortstring;
+begin
+ s:= getShortString();
+ if s[0] = #0 then exit;
+ cmd.cmd:= state.cmd;
+ cmd.param1:= strToInt(s);
+ sendUI(mtNetData, @cmd, sizeof(cmd));
+ handleTail()
+end;
+
+procedure handler__UNKNOWN_;
+begin
+ //writeln('[NET] Unknown cmd');
+ handleTail();
+ state.l:= 0
+end;
+
+const net2cmd: array[0..46] of TCmdType = (cmd_WARNING, cmd_WARNING,
+ cmd_TEAM_COLOR, cmd_TEAM_ACCEPTED, cmd_SERVER_VARS, cmd_SERVER_MESSAGE,
+ cmd_SERVER_AUTH, cmd_RUN_GAME, cmd_ROUND_FINISHED, cmd_ROOM_UPD, cmd_ROOM_DEL,
+ cmd_ROOM_ADD, cmd_ROOMS, cmd_REMOVE_TEAM, cmd_PROTO, cmd_PING, cmd_NOTICE,
+ cmd_NICK, cmd_LOBBY_LEFT, cmd_LOBBY_JOINED, cmd_LEFT, cmd_KICKED, cmd_JOINING,
+ cmd_JOINED, cmd_INFO, cmd_HH_NUM, cmd_ERROR, cmd_EM, cmd_CONNECTED,
+ cmd_CLIENT_FLAGS, cmd_CHAT, cmd_CFG_THEME, cmd_CFG_TEMPLATE, cmd_CFG_SEED,
+ cmd_CFG_SCRIPT, cmd_CFG_SCHEME, cmd_CFG_MAZE_SIZE, cmd_CFG_MAP, cmd_CFG_MAPGEN,
+ cmd_CFG_FULLMAPCONFIG, cmd_CFG_FEATURE_SIZE, cmd_CFG_DRAWNMAP, cmd_CFG_AMMO,
+ cmd_BYE, cmd_BANLIST, cmd_ASKPASSWORD, cmd_ADD_TEAM);
+const letters: array[0..332] of char = ('A', 'D', 'D', '_', 'T', 'E', 'A', 'M',
+ #10, 'S', 'K', 'P', 'A', 'S', 'S', 'W', 'O', 'R', 'D', #10, 'B', 'A', 'N', 'L',
+ 'I', 'S', 'T', #10, 'Y', 'E', #10, 'C', 'F', 'G', #10, 'A', 'M', 'M', 'O', #10,
+ 'D', 'R', 'A', 'W', 'N', 'M', 'A', 'P', #10, 'F', 'E', 'A', 'T', 'U', 'R', 'E',
+ '_', 'S', 'I', 'Z', 'E', #10, 'U', 'L', 'L', 'M', 'A', 'P', 'C', 'O', 'N', 'F',
+ 'I', 'G', #10, 'M', 'A', 'P', 'G', 'E', 'N', #10, #10, 'Z', 'E', '_', 'S', 'I',
+ 'Z', 'E', #10, 'S', 'C', 'H', 'E', 'M', 'E', #10, 'R', 'I', 'P', 'T', #10, 'E',
+ 'E', 'D', #10, 'T', 'E', 'M', 'P', 'L', 'A', 'T', 'E', #10, 'H', 'E', 'M', 'E',
+ #10, 'H', 'A', 'T', #10, 'L', 'I', 'E', 'N', 'T', '_', 'F', 'L', 'A', 'G', 'S',
+ #10, 'O', 'N', 'N', 'E', 'C', 'T', 'E', 'D', #10, 'E', 'M', #10, 'R', 'R', 'O',
+ 'R', #10, 'H', 'H', '_', 'N', 'U', 'M', #10, 'I', 'N', 'F', 'O', #10, 'J', 'O',
+ 'I', 'N', 'E', 'D', #10, 'I', 'N', 'G', #10, 'K', 'I', 'C', 'K', 'E', 'D', #10,
+ 'L', 'E', 'F', 'T', #10, 'O', 'B', 'B', 'Y', ':', 'J', 'O', 'I', 'N', 'E', 'D',
+ #10, 'L', 'E', 'F', 'T', #10, 'N', 'I', 'C', 'K', #10, 'O', 'T', 'I', 'C', 'E',
+ #10, 'P', 'I', 'N', 'G', #10, 'R', 'O', 'T', 'O', #10, 'R', 'E', 'M', 'O', 'V',
+ 'E', '_', 'T', 'E', 'A', 'M', #10, 'O', 'O', 'M', 'S', #10, #10, 'A', 'D', 'D',
+ #10, 'D', 'E', 'L', #10, 'U', 'P', 'D', #10, 'U', 'N', 'D', '_', 'F', 'I', 'N',
+ 'I', 'S', 'H', 'E', 'D', #10, 'U', 'N', '_', 'G', 'A', 'M', 'E', #10, 'S', 'E',
+ 'R', 'V', 'E', 'R', '_', 'A', 'U', 'T', 'H', #10, 'M', 'E', 'S', 'S', 'A', 'G',
+ 'E', #10, 'V', 'A', 'R', 'S', #10, 'T', 'E', 'A', 'M', '_', 'A', 'C', 'C', 'E',
+ 'P', 'T', 'E', 'D', #10, 'C', 'O', 'L', 'O', 'R', #10, 'W', 'A', 'R', 'N', 'I',
+ 'N', 'G', #10, #0, #10);
+const commands: array[0..332] of integer = (20, 8, 0, 0, 0, 0, 0, 0, -56, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, -55, 11, 7, 0, 0, 0, 0, 0, -54, 0, 0, -53, 115, 89, 0,
+ 0, 5, 0, 0, 0, -52, 9, 0, 0, 0, 0, 0, 0, 0, -51, 26, 12, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, -50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -49, 16, 0, 6, 4, 0, 0, -48, -47,
+ 0, 0, 0, 0, 0, 0, 0, -46, 16, 11, 5, 0, 0, 0, -45, 0, 0, 0, 0, -44, 0, 0, 0,
+ -43, 0, 8, 0, 0, 0, 0, 0, 0, -42, 0, 0, 0, 0, -41, 4, 0, 0, -40, 12, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, -39, 0, 0, 0, 0, 0, 0, 0, 0, -38, 8, 2, -37, 0, 0, 0, 0, -36,
+ 7, 0, 0, 0, 0, 0, -35, 5, 0, 0, 0, -34, 11, 0, 0, 0, 3, 0, -33, 0, 0, 0, -32, 7,
+ 0, 0, 0, 0, 0, -31, 22, 4, 0, 0, -30, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, -29, 0,
+ 0, 0, 0, -28, 11, 4, 0, 0, -27, 0, 0, 0, 0, 0, -26, 10, 4, 0, 0, -25, 0, 0, 0,
+ 0, -24, 51, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, -23, 31, 17, 0, 2, -22, 0, 4, 0, 0,
+ -21, 4, 0, 0, -20, 0, 0, 0, -19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -18, 0, 0,
+ 0, 0, 0, 0, 0, -17, 25, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, -16, 8, 0, 0, 0, 0, 0, 0,
+ -15, 0, 0, 0, 0, -14, 20, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, -13, 0, 0, 0, 0,
+ 0, -12, 8, 0, 0, 0, 0, 0, 0, -11, 0, -10);
+const handlers: array[0..46] of PHandler = (@handler__UNKNOWN_, @handler_L,
+ @handler_SS, @handler_S, @handler_SL, @handler_L, @handler_S, @handler_,
+ @handler_, @handler_MS, @handler_S, @handler_MS, @handler_MS, @handler_S,
+ @handler_i, @handler_MS, @handler_L, @handler_S, @handler_SL, @handler_MS,
+ @handler_SL, @handler_, @handler_S, @handler_MS, @handler_MS, @handler_SS,
+ @handler_L, @handler_ML, @handler__i, @handler_SMS, @handler_SL, @handler_S,
+ @handler_i, @handler_S, @handler_S, @handler_MS, @handler_i, @handler_i,
+ @handler_S, @handler_ML, @handler_i, @handler_L, @handler_SL, @handler_SL,
+ @handler_MS, @handler_S, @handler_MS);
+
+procedure handleTail;
+var cnt: Longint;
+ c: char;
+begin
+ c:= getCurrChar;
+ repeat
+ if c = #10 then cnt:= 0 else cnt:= 1;
+ repeat
+ c:= getNextChar;
+ inc(cnt)
+ until (c = #0) or (c = #10);
+ until (c = #0) or (cnt = 1);
+ state.l:= 0
+end;
+
+var sock: PTCPSocket;
+ netReaderThread: PSDL_Thread;
+
+function getCurrChar: char;
+begin
+ getCurrChar:= state.buf[state.bufpos]
+end;
+
+function getNextChar: char;
+var r: byte;
+begin
+ if state.bufpos < byte(state.buf[0]) then
+ begin
+ inc(state.bufpos);
+ end else
+ begin
+ r:= SDLNet_TCP_Recv(sock, @state.buf[1], 255);
+ if r > 0 then
+ begin
+ state.bufpos:= 1;
+ state.buf[0]:= char(r);
+ end else
+ begin
+ state.bufpos:= 0;
+ state.buf[0]:= #0;
+ end
+ end;
+
+ getNextChar:= state.buf[state.bufpos];
+end;
+
+function netReader(data: pointer): LongInt; cdecl; export;
+var c: char;
+ ipaddr: TIPAddress;
+begin
+ netReader:= 0;
+
+ if SDLNet_ResolveHost(ipaddr, PChar('netserver.hedgewars.org'), 46631) = 0 then
+ sock:= SDLNet_TCP_Open(ipaddr);
+
+ repeat
+ c:= getNextChar;
+ //writeln('>>>>> ', c, ' [', letters[state.l], '] ', commands[state.l], ' ', state.l);
+ if c = #0 then
+ isConnected:= false
+ else
+ begin
+ while (letters[state.l] <> c) and (commands[state.l] > 0) do
+ inc(state.l, commands[state.l]);
+
+ if c = letters[state.l] then
+ if commands[state.l] < 0 then
+ begin
+ state.cmd:= net2cmd[-10 - commands[state.l]];
+ //writeln('[NET] ', state.cmd);
+ handlers[-10 - commands[state.l]]();
+ state.l:= 0
+ end
+ else
+ inc(state.l)
+ else
+ begin
+ handler__UNKNOWN_()
+ end
+ end
+ until not isConnected;
+
+ SDLNet_TCP_Close(sock);
+ sock:= nil;
+
+ writeln('[NET] netReader: disconnected');
+end;
+
+procedure sendNet(s: shortstring);
+begin
+ writeln('[NET] Send: ', s);
+ ipcToNet(s + endCmd);
+end;
+
+procedure sendNetLn(s: shortstring);
+begin
+ writeln('[NET] Send: ', s);
+ ipcToNet(s + #10);
+end;
+
+function getShortString: shortstring;
+var s: shortstring;
+ c: char;
+begin
+ s[0]:= #0;
+
+ repeat
+ inc(s[0]);
+ s[byte(s[0])]:= getNextChar
+ until (s[0] = #255) or (s[byte(s[0])] = #10) or (s[byte(s[0])] = #0);
+
+ if s[byte(s[0])] = #10 then
+ dec(s[0])
+ else
+ repeat c:= getNextChar until (c = #0) or (c = #10);
+
+ getShortString:= s
+end;
+
+function getLongString: ansistring;
+var s: shortstring;
+ l: ansistring;
+ c: char;
+begin
+ l:= '';
+
+ repeat
+ s[0]:= #0;
+ repeat
+ inc(s[0]);
+ c:= getNextChar;
+ s[byte(s[0])]:= c
+ until (s[0] = #255) or (c = #10) or (c = #0);
+
+ if s[byte(s[0])] = #10 then
+ dec(s[0]);
+
+ l:= l + s
+ until (c = #10) or (c = #0);
+
+ getLongString:= l
+end;
+
+procedure netSendCallback(p: pointer; msg: PChar; len: Longword);
+begin
+ // W A R N I N G: totally thread-unsafe due to use of sock variable
+ SDLNet_TCP_Send(sock, msg, len);
+end;
+
+procedure connectOfficialServer;
+begin
+ if sock <> nil then
+ exit;
+
+ state.bufpos:= 0;
+ state.buf:= '';
+
+ state.l:= 0;
+ isConnected:= true;
+
+ netReaderThread:= SDL_CreateThread(@netReader, 'netReader', nil);
+ SDL_DetachThread(netReaderThread)
+end;
+
+procedure initModule;
+begin
+ sock:= nil;
+ isConnected:= false;
+
+ SDLNet_Init;
+
+ registerNetCallback(nil, @netSendCallback);
+end;
+
+procedure freeModule;
+begin
+end;
+
+end.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hedgewars/uFLNetProtocol.pas Sun Dec 06 20:36:21 2015 +0300
@@ -0,0 +1,556 @@
+unit uFLNetProtocol;
+interface
+
+procedure passNetData(p: pointer); cdecl;
+
+procedure sendChatLine(msg: PChar); cdecl;
+procedure joinRoom(roomName: PChar); cdecl;
+procedure partRoom(msg: PChar); cdecl;
+
+procedure ResetNetState;
+
+implementation
+uses uFLNetTypes, uFLTypes, uFLUICallback, uFLNet, uFLGameConfig, uFLUtils;
+
+type
+ PHandler = procedure (var t: TCmdData);
+
+var isInRoom: boolean;
+ myNickname: shortstring;
+
+procedure onRoomLeaving();
+begin
+ isInRoom:= false;
+ sendUI(mtMoveToLobby, nil, 0);
+ netResetTeams
+end;
+
+var teamIndex: LongInt;
+ tmpTeam: TTeam;
+
+const teamFields: array[0..22] of PShortstring = (
+ @tmpTeam.teamName
+ , @tmpTeam.grave
+ , @tmpTeam.fort
+ , @tmpTeam.voice
+ , @tmpTeam.flag
+ , @tmpTeam.owner
+ , nil
+ , @tmpTeam.hedgehogs[0].name
+ , @tmpTeam.hedgehogs[0].hat
+ , @tmpTeam.hedgehogs[1].name
+ , @tmpTeam.hedgehogs[1].hat
+ , @tmpTeam.hedgehogs[2].name
+ , @tmpTeam.hedgehogs[2].hat
+ , @tmpTeam.hedgehogs[3].name
+ , @tmpTeam.hedgehogs[3].hat
+ , @tmpTeam.hedgehogs[4].name
+ , @tmpTeam.hedgehogs[4].hat
+ , @tmpTeam.hedgehogs[5].name
+ , @tmpTeam.hedgehogs[5].hat
+ , @tmpTeam.hedgehogs[6].name
+ , @tmpTeam.hedgehogs[6].hat
+ , @tmpTeam.hedgehogs[7].name
+ , @tmpTeam.hedgehogs[7].hat
+ );
+procedure handler_ADD_TEAM(var p: TCmdParam);
+begin
+ teamIndex:= 0;
+ tmpTeam.extDriven:= true;
+ tmpTeam.color:= 0
+end;
+
+procedure handler_ADD_TEAM_s(var s: TCmdParamS);
+begin
+ if teamIndex = 6 then
+ tmpTeam.botLevel:= strToInt(s.str1)
+ else if teamIndex < 23 then
+ teamFields[teamIndex]^:= s.str1;
+
+ if teamIndex = 22 then
+ netAddTeam(tmpTeam);
+
+ inc(teamIndex);
+end;
+
+procedure handler_ASKPASSWORD(var p: TCmdParamS);
+begin
+end;
+
+procedure handler_BANLIST(var p: TCmdParam);
+begin
+end;
+
+procedure handler_BANLIST_s(var s: TCmdParamS);
+begin
+end;
+
+procedure handler_BYE(var p: TCmdParamSL);
+begin
+ sendUI(mtDisconnected, @p.str2[1], length(p.str2));
+end;
+
+procedure handler_CFG_AMMO(var p: TCmdParamSL);
+begin
+ netSetAmmo(p.str1, p.str2)
+end;
+
+procedure handler_CFG_DRAWNMAP(var p: TCmdParamL);
+begin
+end;
+
+procedure handler_CFG_FEATURE_SIZE(var p: TCmdParami);
+begin
+ if isInRoom then
+ begin
+ netSetFeatureSize(p.param1);
+ updatePreviewIfNeeded
+ end;
+end;
+
+var fmcfgIndex: integer;
+
+procedure handler_CFG_FULLMAPCONFIG(var p: TCmdParam);
+begin
+ fmcfgIndex:= 0;
+end;
+
+procedure handler_CFG_FULLMAPCONFIG_s(var s: TCmdParamS);
+begin
+ if not isInRoom then exit;
+
+ inc(fmcfgIndex);
+ case fmcfgIndex of
+ 1: netSetFeatureSize(strToInt(s.str1));
+ 2: if s.str1[0] <> '+' then netSetMap(s.str1);
+ 3: netSetMapGen(strToInt(s.str1));
+ 4: netSetMazeSize(strToInt(s.str1));
+ 5: netSetSeed(s.str1);
+ 6: begin
+ netSetTemplate(strToInt(s.str1));
+ updatePreviewIfNeeded;
+ end;
+ end;
+end;
+
+procedure handler_CFG_MAP(var p: TCmdParamS);
+begin
+ if isInRoom then
+ netSetMap(p.str1);
+end;
+
+procedure handler_CFG_MAPGEN(var p: TCmdParami);
+begin
+ if isInRoom then
+ begin
+ netSetMapGen(p.param1);
+ updatePreviewIfNeeded
+ end
+end;
+
+procedure handler_CFG_MAZE_SIZE(var p: TCmdParami);
+begin
+ if isInRoom then
+ begin
+ netSetMazeSize(p.param1);
+ updatePreviewIfNeeded
+ end
+end;
+
+var schemeIndex: LongInt;
+ tmpScheme: TScheme;
+
+procedure handler_CFG_SCHEME(var p: TCmdParam);
+begin
+ schemeIndex:= 0
+end;
+
+const schemeFields: array[0..43] of pointer = (
+ @tmpScheme.schemeName // 0
+ , @tmpScheme.fortsmode // 1
+ , @tmpScheme.divteams // 2
+ , @tmpScheme.solidland // 3
+ , @tmpScheme.border // 4
+ , @tmpScheme.lowgrav // 5
+ , @tmpScheme.laser // 6
+ , @tmpScheme.invulnerability // 7
+ , @tmpScheme.resethealth // 8
+ , @tmpScheme.vampiric // 9
+ , @tmpScheme.karma // 10
+ , @tmpScheme.artillery // 11
+ , @tmpScheme.randomorder // 12
+ , @tmpScheme.king // 13
+ , @tmpScheme.placehog // 14
+ , @tmpScheme.sharedammo // 15
+ , @tmpScheme.disablegirders // 16
+ , @tmpScheme.disablelandobjects // 17
+ , @tmpScheme.aisurvival // 18
+ , @tmpScheme.infattack // 19
+ , @tmpScheme.resetweps // 20
+ , @tmpScheme.perhogammo // 21
+ , @tmpScheme.disablewind // 22
+ , @tmpScheme.morewind // 23
+ , @tmpScheme.tagteam // 24
+ , @tmpScheme.bottomborder // 25
+ , @tmpScheme.damagefactor // 26
+ , @tmpScheme.turntime // 27
+ , @tmpScheme.health // 28
+ , @tmpScheme.suddendeath // 29
+ , @tmpScheme.caseprobability // 30
+ , @tmpScheme.minestime // 31
+ , @tmpScheme.minesnum // 32
+ , @tmpScheme.minedudpct // 33
+ , @tmpScheme.explosives // 34
+ , @tmpScheme.airmines // 35
+ , @tmpScheme.healthprobability // 36
+ , @tmpScheme.healthcaseamount // 37
+ , @tmpScheme.waterrise // 38
+ , @tmpScheme.healthdecrease // 39
+ , @tmpScheme.ropepct // 40
+ , @tmpScheme.getawaytime // 41
+ , @tmpScheme.worldedge // 42
+ , @tmpScheme.scriptparam // 43
+ );
+
+procedure handler_CFG_SCHEME_s(var s: TCmdParamS);
+begin
+ if(schemeIndex = 0) then
+ tmpScheme.schemeName:= s.str1
+ else
+ if(schemeIndex = 43) then
+ tmpScheme.scriptparam:= copy(s.str1, 2, length(s.str1) - 1)
+ else
+ if(schemeIndex < 26) then
+ PBoolean(schemeFields[schemeIndex])^:= s.str1[1] = 't'
+ else
+ if(schemeIndex < 43) then
+ PLongInt(schemeFields[schemeIndex])^:= strToInt(s.str1);
+
+ if(schemeIndex = 43) then
+ netSetScheme(tmpScheme);
+
+ inc(schemeIndex);
+end;
+
+procedure handler_CFG_SCRIPT(var p: TCmdParamS);
+begin
+ if isInRoom then
+ netSetScript(p.str1)
+end;
+
+procedure handler_CFG_SEED(var p: TCmdParamS);
+begin
+ if isInRoom then
+ netSetSeed(p.str1)
+end;
+
+procedure handler_CFG_TEMPLATE(var p: TCmdParami);
+begin
+ if isInRoom then
+ begin
+ netSetTemplate(p.param1);
+ updatePreviewIfNeeded
+ end
+end;
+
+procedure handler_CFG_THEME(var p: TCmdParamS);
+begin
+ if isInRoom then
+ netSetTheme(p.str1)
+end;
+
+procedure handler_CHAT(var p: TCmdParamSL);
+var s: string;
+begin
+ s:= p.str1 + #10 + p.str2;
+ if isInRoom then
+ sendUI(mtRoomChatLine, @s[1], length(s))
+ else
+ sendUI(mtLobbyChatLine, @s[1], length(s));
+end;
+
+procedure handler_CLIENT_FLAGS(var p: TCmdParamS);
+begin
+end;
+
+procedure handler_CLIENT_FLAGS_s(var s: TCmdParamS);
+begin
+end;
+
+procedure handler_CONNECTED(var p: TCmdParami);
+begin
+ sendUI(mtConnected, nil, 0);
+ writeln('Server features version ', p.param1);
+ sendNet('PROTO' + #10 + '51');
+ sendNet('NICK' + #10 + 'qmlfrontend');
+end;
+
+procedure handler_EM(var p: TCmdParam);
+begin
+end;
+
+procedure handler_EM_s(var s: TCmdParamS);
+begin
+end;
+
+procedure handler_ERROR(var p: TCmdParamL);
+begin
+ sendUI(mtError, @p.str1[1], length(p.str1));
+end;
+
+procedure handler_HH_NUM(var p: TCmdParamSS);
+begin
+ netSetHedgehogsNumber(p.str1, StrToInt(p.str2))
+end;
+
+procedure handler_INFO(var p: TCmdParam);
+begin
+end;
+
+procedure handler_INFO_s(var s: TCmdParamS);
+begin
+end;
+
+procedure handler_JOINED(var p: TCmdParam);
+begin
+end;
+
+procedure handler_JOINED_s(var s: TCmdParamS);
+begin
+ if s.str1 = myNickname then // we joined a room
+ begin
+ isInRoom:= true;
+ sendUI(mtMoveToRoom, nil, 0);
+ end;
+
+ sendUI(mtAddRoomClient, @s.str1[1], length(s.str1));
+end;
+
+procedure handler_JOINING(var p: TCmdParamS);
+begin
+end;
+
+procedure handler_KICKED(var p: TCmdParam);
+begin
+ onRoomLeaving()
+end;
+
+procedure handler_LEFT(var p: TCmdParamSL);
+begin
+ p.str2:= p.str1 + #10 + p.str2;
+ sendUI(mtRemoveRoomClient, @p.str2[1], length(p.str2));
+end;
+
+procedure handler_LOBBY_JOINED(var p: TCmdParam);
+begin
+end;
+
+procedure handler_LOBBY_JOINED_s(var s: TCmdParamS);
+begin
+ if s.str1 = myNickname then
+ begin
+ sendUI(mtMoveToLobby, nil, 0);
+ sendNet('LIST');
+ end;
+
+ sendUI(mtAddLobbyClient, @s.str1[1], length(s.str1));
+end;
+
+procedure handler_LOBBY_LEFT(var p: TCmdParamSL);
+begin
+ p.str2:= p.str1 + #10 + p.str2;
+ sendUI(mtRemoveLobbyClient, @p.str2[1], length(p.str2));
+end;
+
+procedure handler_NICK(var p: TCmdParamS);
+begin
+ myNickname:= p.str1;
+ sendUI(mtNickname, @p.str1[1], length(p.str1));
+end;
+
+procedure handler_NOTICE(var p: TCmdParamL);
+begin
+end;
+
+procedure handler_PING(var p: TCmdParam);
+begin
+ sendNet('PONG')
+end;
+
+procedure handler_PING_s(var s: TCmdParamS);
+begin
+end;
+
+procedure handler_PROTO(var p: TCmdParami);
+begin
+ writeln('Protocol ', p.param1)
+end;
+
+procedure handler_REMOVE_TEAM(var p: TCmdParamS);
+begin
+ netRemoveTeam(p.str1)
+end;
+
+var roomInfo: string;
+ roomLinesCount: integer;
+
+procedure handler_ROOMS(var p: TCmdParam);
+begin
+ roomInfo:= '';
+ roomLinesCount:= 0
+end;
+
+procedure handler_ROOMS_s(var s: TCmdParamS);
+begin
+ roomInfo:= roomInfo + s.str1 + #10;
+
+ if roomLinesCount = 8 then
+ begin
+ sendUI(mtAddRoom, @roomInfo[1], length(roomInfo) - 1);
+ roomLinesCount:= 0;
+ roomInfo:= ''
+ end else inc(roomLinesCount);
+end;
+
+procedure handler_ROOM_ADD(var p: TCmdParam);
+begin
+ roomInfo:= '';
+ roomLinesCount:= 0
+end;
+
+procedure handler_ROOM_ADD_s(var s: TCmdParamS);
+begin
+ roomInfo:= roomInfo + s.str1 + #10;
+ inc(roomLinesCount);
+
+ if roomLinesCount = 9 then
+ begin
+ sendUI(mtAddRoom, @roomInfo[1], length(roomInfo) - 1);
+ roomInfo:= '';
+ roomLinesCount:= 0
+ end;
+end;
+
+procedure handler_ROOM_DEL(var p: TCmdParamS);
+begin
+ sendUI(mtRemoveRoom, @p.str1[1], length(p.str1));
+end;
+
+procedure handler_ROOM_UPD(var p: TCmdParam);
+begin
+ roomInfo:= '';
+ roomLinesCount:= 0
+end;
+
+procedure handler_ROOM_UPD_s(var s: TCmdParamS);
+begin
+ roomInfo:= roomInfo + s.str1 + #10;
+ inc(roomLinesCount);
+
+ if roomLinesCount = 10 then
+ sendUI(mtUpdateRoom, @roomInfo[1], length(roomInfo) - 1);
+end;
+
+procedure handler_ROUND_FINISHED(var p: TCmdParam);
+begin
+end;
+
+procedure handler_RUN_GAME(var p: TCmdParam);
+begin
+end;
+
+procedure handler_SERVER_AUTH(var p: TCmdParamS);
+begin
+end;
+
+procedure handler_SERVER_MESSAGE(var p: TCmdParamL);
+begin
+end;
+
+procedure handler_SERVER_VARS(var p: TCmdParamSL);
+begin
+end;
+
+procedure handler_TEAM_ACCEPTED(var p: TCmdParamS);
+begin
+ netAcceptedTeam(p.str1)
+end;
+
+procedure handler_TEAM_COLOR(var p: TCmdParamSS);
+begin
+ netSetTeamColor(p.str1, StrToInt(p.str2));
+end;
+
+procedure handler_WARNING(var p: TCmdParamL);
+begin
+ sendUI(mtWarning, @p.str1[1], length(p.str1));
+end;
+
+const handlers: array[TCmdType] of PHandler = (PHandler(@handler_ADD_TEAM),
+ PHandler(@handler_ADD_TEAM_s), PHandler(@handler_ASKPASSWORD),
+ PHandler(@handler_BANLIST), PHandler(@handler_BANLIST_s),
+ PHandler(@handler_BYE), PHandler(@handler_CFG_AMMO),
+ PHandler(@handler_CFG_DRAWNMAP), PHandler(@handler_CFG_FEATURE_SIZE),
+ PHandler(@handler_CFG_FULLMAPCONFIG), PHandler(@handler_CFG_FULLMAPCONFIG_s),
+ PHandler(@handler_CFG_MAP), PHandler(@handler_CFG_MAPGEN),
+ PHandler(@handler_CFG_MAZE_SIZE), PHandler(@handler_CFG_SCHEME),
+ PHandler(@handler_CFG_SCHEME_s), PHandler(@handler_CFG_SCRIPT),
+ PHandler(@handler_CFG_SEED), PHandler(@handler_CFG_TEMPLATE),
+ PHandler(@handler_CFG_THEME), PHandler(@handler_CHAT),
+ PHandler(@handler_CLIENT_FLAGS), PHandler(@handler_CLIENT_FLAGS_s),
+ PHandler(@handler_CONNECTED), PHandler(@handler_EM), PHandler(@handler_EM_s),
+ PHandler(@handler_ERROR), PHandler(@handler_HH_NUM), PHandler(@handler_INFO),
+ PHandler(@handler_INFO_s), PHandler(@handler_JOINED),
+ PHandler(@handler_JOINED_s), PHandler(@handler_JOINING),
+ PHandler(@handler_KICKED), PHandler(@handler_LEFT),
+ PHandler(@handler_LOBBY_JOINED), PHandler(@handler_LOBBY_JOINED_s),
+ PHandler(@handler_LOBBY_LEFT), PHandler(@handler_NICK),
+ PHandler(@handler_NOTICE), PHandler(@handler_PING), PHandler(@handler_PING_s),
+ PHandler(@handler_PROTO), PHandler(@handler_REMOVE_TEAM),
+ PHandler(@handler_ROOMS), PHandler(@handler_ROOMS_s),
+ PHandler(@handler_ROOM_ADD), PHandler(@handler_ROOM_ADD_s),
+ PHandler(@handler_ROOM_DEL), PHandler(@handler_ROOM_UPD),
+ PHandler(@handler_ROOM_UPD_s), PHandler(@handler_ROUND_FINISHED),
+ PHandler(@handler_RUN_GAME), PHandler(@handler_SERVER_AUTH),
+ PHandler(@handler_SERVER_MESSAGE), PHandler(@handler_SERVER_VARS),
+ PHandler(@handler_TEAM_ACCEPTED), PHandler(@handler_TEAM_COLOR),
+ PHandler(@handler_WARNING));
+
+procedure passNetData(p: pointer); cdecl;
+begin
+ handlers[TCmdData(p^).cmd.cmd](TCmdData(p^))
+end;
+
+procedure sendChatLine(msg: PChar); cdecl;
+begin
+ sendNetLn('CHAT');
+ sendNet(msg);
+end;
+
+procedure joinRoom(roomName: PChar); cdecl;
+begin
+ sendNetLn('JOIN_ROOM');
+ sendNet(roomName);
+end;
+
+procedure partRoom(msg: PChar); cdecl;
+var s: string;
+begin
+ if isInRoom then
+ begin
+ s:= 'PART';
+ if length(msg) > 0 then
+ s:= s + #10 + msg;
+ sendNet(s);
+
+ onRoomLeaving()
+ end
+end;
+
+procedure ResetNetState;
+begin
+ isInRoom:= false;
+end;
+
+end.
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hedgewars/uFLNetTypes.pas Sun Dec 06 20:36:21 2015 +0300
@@ -0,0 +1,55 @@
+unit uFLNetTypes;
+interface
+
+type TCmdType = (cmd_ADD_TEAM, cmd_ADD_TEAM_s, cmd_ASKPASSWORD, cmd_BANLIST,
+ cmd_BANLIST_s, cmd_BYE, cmd_CFG_AMMO, cmd_CFG_DRAWNMAP, cmd_CFG_FEATURE_SIZE,
+ cmd_CFG_FULLMAPCONFIG, cmd_CFG_FULLMAPCONFIG_s, cmd_CFG_MAP, cmd_CFG_MAPGEN,
+ cmd_CFG_MAZE_SIZE, cmd_CFG_SCHEME, cmd_CFG_SCHEME_s, cmd_CFG_SCRIPT,
+ cmd_CFG_SEED, cmd_CFG_TEMPLATE, cmd_CFG_THEME, cmd_CHAT, cmd_CLIENT_FLAGS,
+ cmd_CLIENT_FLAGS_s, cmd_CONNECTED, cmd_EM, cmd_EM_s, cmd_ERROR, cmd_HH_NUM,
+ cmd_INFO, cmd_INFO_s, cmd_JOINED, cmd_JOINED_s, cmd_JOINING, cmd_KICKED,
+ cmd_LEFT, cmd_LOBBY_JOINED, cmd_LOBBY_JOINED_s, cmd_LOBBY_LEFT, cmd_NICK,
+ cmd_NOTICE, cmd_PING, cmd_PING_s, cmd_PROTO, cmd_REMOVE_TEAM, cmd_ROOMS,
+ cmd_ROOMS_s, cmd_ROOM_ADD, cmd_ROOM_ADD_s, cmd_ROOM_DEL, cmd_ROOM_UPD,
+ cmd_ROOM_UPD_s, cmd_ROUND_FINISHED, cmd_RUN_GAME, cmd_SERVER_AUTH,
+ cmd_SERVER_MESSAGE, cmd_SERVER_VARS, cmd_TEAM_ACCEPTED, cmd_TEAM_COLOR,
+ cmd_WARNING);
+
+ type TCmdParam = packed record
+ cmd: TCmdType;
+ end;
+ type TCmdParamL = packed record
+ cmd: TCmdType;
+ str1: string;
+ end;
+ type TCmdParamS = packed record
+ cmd: TCmdType;
+ str1: shortstring;
+ end;
+ type TCmdParamSL = packed record
+ cmd: TCmdType;
+ str1: shortstring;
+ str2: string;
+ end;
+ type TCmdParamSS = packed record
+ cmd: TCmdType;
+ str1: shortstring;
+ str2: shortstring;
+ end;
+ type TCmdParami = packed record
+ cmd: TCmdType;
+ param1: LongInt;
+ end;
+
+ TCmdData = record
+ case byte of
+ 0: (cmd: TCmdParam);
+ 1: (cpl: TCmdParamL);
+ 2: (cps: TCmdParamS);
+ 3: (cpsl: TCmdParamSL);
+ 4: (cpi: TCmdParami);
+ end;
+
+implementation
+
+end.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hedgewars/uFLRunQueue.pas Sun Dec 06 20:36:21 2015 +0300
@@ -0,0 +1,89 @@
+unit uFLRunQueue;
+interface
+uses uFLTypes;
+
+procedure queueExecution(var config: TGameConfig);
+procedure passFlibEvent(p: pointer); cdecl;
+
+implementation
+uses uFLGameConfig, hwengine, uFLData, uFLUICallback;
+
+var runQueue: PGameConfig = nil;
+
+procedure nextRun;
+begin
+ if runQueue <> nil then
+ begin
+ if runQueue^.gameType = gtPreview then
+ sendUI(mtRenderingPreview, nil, 0);
+
+ RunEngine(runQueue^.argumentsNumber, @runQueue^.argv);
+
+ sendConfig(runQueue)
+ end
+end;
+
+procedure cleanupConfig;
+var t: PGameConfig;
+begin
+ t:= runQueue;
+ runQueue:= t^.nextConfig;
+ dispose(t)
+end;
+
+procedure queueExecution(var config: TGameConfig);
+var pConfig, t, tt: PGameConfig;
+ i: Longword;
+begin
+ new(pConfig);
+ pConfig^:= config;
+
+ with pConfig^ do
+ begin
+ nextConfig:= nil;
+
+ for i:= 0 to Pred(MAXARGS) do
+ begin
+ if arguments[i][0] = #255 then
+ arguments[i][255]:= #0
+ else
+ arguments[i][byte(arguments[i][0]) + 1]:= #0;
+ argv[i]:= @arguments[i][1]
+ end;
+ end;
+
+ if runQueue = nil then
+ begin
+ runQueue:= pConfig;
+
+ nextRun
+ end else
+ begin
+ t:= runQueue;
+ while t^.nextConfig <> nil do
+ begin
+ if (pConfig^.gameType = gtPreview) and (t^.nextConfig^.gameType = gtPreview) then
+ begin
+ tt:= t^.nextConfig;
+ pConfig^.nextConfig:= tt^.nextConfig;
+ t^.nextConfig:= pConfig;
+ dispose(tt);
+ exit // boo
+ end;
+ t:= t^.nextConfig;
+ end;
+ t^.nextConfig:= pConfig
+ end;
+end;
+
+procedure passFlibEvent(p: pointer); cdecl;
+begin
+ case TFLIBEvent(p^) of
+ flibGameFinished: begin
+ cleanupConfig;
+ nextRun
+ end;
+ end;
+end;
+
+end.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hedgewars/uFLSchemes.pas Sun Dec 06 20:36:21 2015 +0300
@@ -0,0 +1,209 @@
+unit uFLSchemes;
+interface
+uses uFLTypes;
+
+function getSchemesList: PPChar; cdecl;
+procedure freeSchemesList;
+
+function schemeByName(s: shortstring): PScheme;
+procedure sendSchemeConfig(var scheme: TScheme);
+
+implementation
+uses uFLUtils, uFLIPC, uPhysFSLayer, uFLData;
+
+const MAX_SCHEME_NAMES = 64;
+type
+ TSchemeArray = array [0..0] of TScheme;
+ PSchemeArray = ^TSchemeArray;
+var
+ schemesList: PScheme;
+ schemesNumber: LongInt;
+ listOfSchemeNames: array[0..MAX_SCHEME_NAMES] of PChar;
+ tmpScheme: TScheme;
+
+const ints: array[0 .. 17] of record
+ name: shortstring;
+ param: ^LongInt;
+ end = (
+ (name: 'damagefactor'; param: @tmpScheme.damagefactor)
+ , (name: 'turntime'; param: @tmpScheme.turntime)
+ , (name: 'health'; param: @tmpScheme.health)
+ , (name: 'suddendeath'; param: @tmpScheme.suddendeath)
+ , (name: 'caseprobability'; param: @tmpScheme.caseprobability)
+ , (name: 'minestime'; param: @tmpScheme.minestime)
+ , (name: 'landadds'; param: @tmpScheme.landadds)
+ , (name: 'minedudpct'; param: @tmpScheme.minedudpct)
+ , (name: 'explosives'; param: @tmpScheme.explosives)
+ , (name: 'minesnum'; param: @tmpScheme.minesnum)
+ , (name: 'healthprobability'; param: @tmpScheme.healthprobability)
+ , (name: 'healthcaseamount'; param: @tmpScheme.healthcaseamount)
+ , (name: 'waterrise'; param: @tmpScheme.waterrise)
+ , (name: 'healthdecrease'; param: @tmpScheme.healthdecrease)
+ , (name: 'ropepct'; param: @tmpScheme.ropepct)
+ , (name: 'getawaytime'; param: @tmpScheme.getawaytime)
+ , (name: 'worldedge'; param: @tmpScheme.worldedge)
+ , (name: 'airmines'; param: @tmpScheme.airmines)
+ );
+const bools: array[0 .. 25] of record
+ name: shortstring;
+ param: ^boolean;
+ end = (
+ (name: 'fortsmode'; param: @tmpScheme.fortsmode)
+ , (name: 'divteams'; param: @tmpScheme.divteams)
+ , (name: 'solidland'; param: @tmpScheme.solidland)
+ , (name: 'border'; param: @tmpScheme.border)
+ , (name: 'lowgrav'; param: @tmpScheme.lowgrav)
+ , (name: 'laser'; param: @tmpScheme.laser)
+ , (name: 'invulnerability'; param: @tmpScheme.invulnerability)
+ , (name: 'mines'; param: @tmpScheme.mines)
+ , (name: 'vampiric'; param: @tmpScheme.vampiric)
+ , (name: 'karma'; param: @tmpScheme.karma)
+ , (name: 'artillery'; param: @tmpScheme.artillery)
+ , (name: 'randomorder'; param: @tmpScheme.randomorder)
+ , (name: 'king'; param: @tmpScheme.king)
+ , (name: 'placehog'; param: @tmpScheme.placehog)
+ , (name: 'sharedammo'; param: @tmpScheme.sharedammo)
+ , (name: 'disablegirders'; param: @tmpScheme.disablegirders)
+ , (name: 'disablewind'; param: @tmpScheme.disablewind)
+ , (name: 'morewind'; param: @tmpScheme.morewind)
+ , (name: 'tagteam'; param: @tmpScheme.tagteam)
+ , (name: 'bottomborder'; param: @tmpScheme.bottomborder)
+ , (name: 'resethealth'; param: @tmpScheme.resethealth)
+ , (name: 'disablelandobjects'; param: @tmpScheme.disablelandobjects)
+ , (name: 'aisurvival'; param: @tmpScheme.aisurvival)
+ , (name: 'infattack'; param: @tmpScheme.infattack)
+ , (name: 'resetweps'; param: @tmpScheme.resetweps)
+ , (name: 'perhogammo'; param: @tmpScheme.perhogammo)
+ );
+
+
+procedure loadSchemes;
+var f: PFSFile;
+ scheme: PScheme;
+ schemes: PSchemeArray;
+ s: shortstring;
+ l, i, ii: Longword;
+ isFound: boolean;
+begin
+ f:= pfsOpenRead('/Config/schemes.ini');
+ schemesNumber:= 0;
+
+ if f <> nil then
+ begin
+ while (not pfsEOF(f)) and (schemesNumber = 0) do
+ begin
+ pfsReadLn(f, s);
+
+ if copy(s, 1, 5) = 'size=' then
+ schemesNumber:= strToInt(midStr(s, 6));
+ end;
+
+ //inc(schemesNumber); // add some default schemes
+
+ schemesList:= GetMem(sizeof(schemesList^) * (schemesNumber + 1));
+ schemes:= PSchemeArray(schemesList);
+
+ while (not pfsEOF(f)) do
+ begin
+ pfsReadLn(f, s);
+
+ i:= 1;
+ while(i <= length(s)) and (s[i] <> '\') do inc(i);
+
+ if i < length(s) then
+ begin
+ l:= strToInt(copy(s, 1, i - 1));
+ delete(s, 1, i);
+
+ if (l <= schemesNumber) and (l > 0) then
+ begin
+ if copy(s, 1, 5) = 'name=' then
+ schemes^[l - 1].schemeName:= midStr(s, 6)
+ else if copy(s, 1, 12) = 'scriptparam=' then
+ schemes^[l - 1].scriptparam:= midStr(s, 13) else
+ begin
+ ii:= 0;
+ repeat
+ isFound:= readInt(ints[ii].name, s, PLongInt(ints[ii].param - @tmpScheme + @schemes^[l - 1])^);
+ inc(ii)
+ until isFound or (ii > High(ints));
+
+ if not isFound then
+ begin
+ ii:= 0;
+ repeat
+ isFound:= readBool(bools[ii].name, s, PBoolean(bools[ii].param - @tmpScheme + @schemes^[l - 1])^);
+ inc(ii)
+ until isFound or (ii > High(bools));
+ end;
+ end;
+ end;
+ end;
+ end;
+
+ pfsClose(f)
+ end;
+end;
+
+
+function getSchemesList: PPChar; cdecl;
+var i, t, l: Longword;
+ scheme: PScheme;
+begin
+ if schemesList = nil then
+ loadSchemes;
+
+ t:= schemesNumber;
+ if t >= MAX_SCHEME_NAMES then
+ t:= MAX_SCHEME_NAMES;
+
+ scheme:= schemesList;
+ for i:= 0 to Pred(t) do
+ begin
+ l:= length(scheme^.schemeName);
+ if l >= 255 then l:= 254;
+ scheme^.schemeName[l + 1]:= #0;
+ listOfSchemeNames[i]:= @scheme^.schemeName[1];
+ inc(scheme)
+ end;
+
+ listOfSchemeNames[t]:= nil;
+
+ getSchemesList:= listOfSchemeNames
+end;
+
+function schemeByName(s: shortstring): PScheme;
+var i: Longword;
+ scheme: PScheme;
+begin
+ scheme:= schemesList;
+ i:= 0;
+ while (i < schemesNumber) and (scheme^.schemeName <> s) do
+ begin
+ inc(scheme);
+ inc(i)
+ end;
+
+ if i < schemesNumber then schemeByName:= scheme else schemeByName:= nil
+end;
+
+procedure freeSchemesList;
+begin
+ if schemesList <> nil then
+ FreeMem(schemesList, sizeof(schemesList^) * (schemesNumber + 1))
+end;
+
+
+procedure sendSchemeConfig(var scheme: TScheme);
+var i: Longword;
+begin
+ with scheme do
+ begin
+ if scheme.turntime <> 45 then
+ ipcToEngine('e$turntime ' + inttostr(scheme.turntime * 1000));
+ if scheme.minesnum <> 4 then
+ ipcToEngine('e$minesnum ' + inttostr(scheme.minesnum));
+ end
+end;
+
+end.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hedgewars/uFLScripts.pas Sun Dec 06 20:36:21 2015 +0300
@@ -0,0 +1,128 @@
+unit uFLScripts;
+interface
+uses uFLTypes;
+
+function getScriptsList: PPChar; cdecl;
+procedure freeScriptsList;
+
+implementation
+uses uFLUtils, uFLIPC, uPhysFSLayer, uFLData;
+
+const MAX_SCRIPT_NAMES = 64;
+type
+ TScript = record
+ scriptName: shortstring;
+ description: shortstring;
+ gameScheme, weapons: shortstring;
+ end;
+ PScript = ^TScript;
+var
+ scriptsList: PScript;
+ scriptsNumber: Longword;
+ listOfScriptNames: array[0..MAX_SCRIPT_NAMES] of PChar;
+
+procedure loadScript(var script: TScript; scriptName, fileName: shortstring);
+var f: PFSFile;
+begin
+ underScore2Space(scriptName);
+ script.scriptName:= scriptName;
+ script.description:= scriptName + ' script description';
+
+ f:= pfsOpenRead(copy(fileName, 1, length(fileName) - 4) + '.txt');
+
+ script.gameScheme:= '';
+ script.weapons:= '';
+
+ if f <> nil then
+ begin
+ if not pfsEOF(f) then
+ begin
+ pfsReadLn(f, script.gameScheme);
+
+ if not pfsEOF(f) then
+ pfsReadLn(f, script.weapons);
+ end;
+
+ pfsClose(f)
+ end
+end;
+
+procedure loadScripts;
+var filesList, tmp: PPChar;
+ script: PScript;
+ s: shortstring;
+ l: Longword;
+begin
+ filesList:= pfsEnumerateFiles('/Scripts/Multiplayer');
+ scriptsNumber:= 1;
+
+ tmp:= filesList;
+ while tmp^ <> nil do
+ begin
+ s:= shortstring(tmp^);
+ l:= length(s);
+ if (l > 4) and (copy(s, l - 3, 4) = '.lua') then inc(scriptsNumber);
+ inc(tmp)
+ end;
+
+ scriptsList:= GetMem(sizeof(scriptsList^) * (scriptsNumber + 1));
+
+ script:= scriptsList;
+
+ // add 'normal' script
+ script^.scriptName:= 'Normal';
+ script^.description:= 'Normal gameplay';
+ inc(script);
+
+ // fill the rest from *.lua list
+ tmp:= filesList;
+ while tmp^ <> nil do
+ begin
+ s:= shortstring(tmp^);
+ l:= length(s);
+ if (l > 4) and (copy(s, l - 3, 4) = '.lua') then
+ begin
+ loadScript(script^, copy(s, 1, l - 4), '/Config/Scripts/' + s);
+ inc(script)
+ end;
+ inc(tmp)
+ end;
+
+ pfsFreeList(filesList)
+end;
+
+
+function getScriptsList: PPChar; cdecl;
+var i, t, l: Longword;
+ script: PScript;
+begin
+ if scriptsList = nil then
+ loadScripts;
+
+ t:= scriptsNumber;
+ if t >= MAX_SCRIPT_NAMES then
+ t:= MAX_SCRIPT_NAMES;
+
+ script:= scriptsList;
+ for i:= 0 to Pred(t) do
+ begin
+ l:= length(script^.scriptName);
+ if l >= 255 then l:= 254;
+ script^.scriptName[l + 1]:= #0;
+ listOfScriptNames[i]:= @script^.scriptName[1];
+ inc(script)
+ end;
+
+ listOfScriptNames[t]:= nil;
+
+ getScriptsList:= listOfScriptNames
+end;
+
+
+procedure freeScriptsList;
+begin
+ if scriptsList <> nil then
+ FreeMem(scriptsList, sizeof(scriptsList^) * scriptsNumber)
+end;
+
+end.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hedgewars/uFLTeams.pas Sun Dec 06 20:36:21 2015 +0300
@@ -0,0 +1,221 @@
+unit uFLTeams;
+interface
+uses uFLTypes;
+
+function createRandomTeam: TTeam;
+procedure sendTeamConfig(var team: TTeam);
+
+function getTeamsList: PPChar; cdecl;
+procedure freeTeamsList;
+
+function teamByName(s: shortstring): PTeam;
+
+procedure sendTeam(var team: TTeam);
+procedure removeTeam(teamName: shortstring);
+
+implementation
+uses uFLUtils, uFLIPC, uPhysFSLayer, uFLData, uFLNet;
+
+const MAX_TEAM_NAMES = 128;
+var
+ teamsList: PTeam;
+ teamsNumber: Longword;
+ listOfTeamNames: array[0..MAX_TEAM_NAMES] of PChar;
+
+
+function createRandomTeam: TTeam;
+var t: TTeam;
+ i: Longword;
+begin
+ with t do
+ begin
+ teamName:= 'team' + inttostr(random(100));
+
+ for i:= 0 to 7 do
+ with hedgehogs[i] do
+ begin
+ name:= 'hedgehog ' + inttostr(i);
+ hat:= 'NoHat'
+ end;
+
+ botLevel:= 0;
+ hogsNumber:= 4
+ end;
+ createRandomTeam:= t
+end;
+
+
+procedure sendTeamConfig(var team: TTeam);
+var i: Longword;
+begin
+ with team do
+ begin
+ ipcToEngine('eaddteam <hash> ' + colorsSet[color] + ' ' + teamName);
+ for i:= 0 to Pred(hogsNumber) do
+ begin
+ ipcToEngine('eaddhh ' + inttostr(botLevel) + ' 100 hog');// + hedgehogs[i].name);
+ //ipcToEngine('ehat ' + hedgehogs[i].hat);
+ end;
+ end
+end;
+
+
+procedure loadTeam(var team: TTeam; fileName: shortstring);
+var f: PFSFile;
+ section: LongInt;
+ l: shortstring;
+begin
+ section:= -1;
+ f:= pfsOpenRead(fileName);
+
+ while (not pfsEOF(f)) do
+ begin
+ pfsReadLn(f, l);
+
+ if l = '' then
+ else if l = '[Team]' then
+ section:= -2
+ else if copy(l, 1, 9) = '[Hedgehog' then
+ section:= StrToInt(copy(l, 10, 1))
+ else if section = -2 then
+ begin // [Team]
+ if copy(l, 1, 5) = 'Name=' then
+ team.teamName:= midStr(l, 6)
+ else if copy(l, 1, 6) = 'Grave=' then
+ team.grave:= midStr(l, 7)
+ else if copy(l, 1, 5) = 'Fort=' then
+ team.fort:= midStr(l, 6)
+ else if copy(l, 1, 5) = 'Flag=' then
+ team.flag:= midStr(l, 6)
+ else if copy(l, 1, 10) = 'Voicepack=' then
+ team.voice:= midStr(l, 11)
+ else if copy(l, 1, 11) = 'Difficulty=' then
+ team.botLevel:= StrToInt(midStr(l, 12))
+ end else if (section >= 0) and (section <= 7) then
+ begin // [Hedgehog*]
+ if copy(l, 1, 5) = 'Name=' then
+ team.hedgehogs[section].name:= midStr(l, 6)
+ else if copy(l, 1, 4) = 'Hat=' then
+ team.hedgehogs[section].hat:= midStr(l, 5)
+ end;
+ end;
+
+ pfsClose(f)
+end;
+
+
+procedure loadTeams;
+var filesList, tmp: PPChar;
+ team: PTeam;
+ s: shortstring;
+ l: Longword;
+begin
+ filesList:= pfsEnumerateFiles('/Config/Teams');
+ teamsNumber:= 0;
+
+ tmp:= filesList;
+ while tmp^ <> nil do
+ begin
+ s:= shortstring(tmp^);
+ l:= length(s);
+ if (l > 4) and (copy(s, l - 3, 4) = '.hwt') then inc(teamsNumber);
+ inc(tmp)
+ end;
+
+ // TODO: no teams at all?
+ teamsList:= GetMem(sizeof(teamsList^) * teamsNumber);
+
+ team:= teamsList;
+ tmp:= filesList;
+ while tmp^ <> nil do
+ begin
+ s:= shortstring(tmp^);
+ l:= length(s);
+ if (l > 4) and (copy(s, l - 3, 4) = '.hwt') then
+ begin
+ loadTeam(team^, '/Config/Teams/' + s);
+ inc(team)
+ end;
+ inc(tmp)
+ end;
+
+ pfsFreeList(filesList)
+end;
+
+
+function getTeamsList: PPChar; cdecl;
+var i, t, l: Longword;
+ team: PTeam;
+begin
+ if teamsList = nil then
+ loadTeams;
+
+ t:= teamsNumber;
+ if t >= MAX_TEAM_NAMES then
+ t:= MAX_TEAM_NAMES;
+
+ team:= teamsList;
+ for i:= 0 to Pred(t) do
+ begin
+ l:= length(team^.teamName);
+ if l >= 255 then l:= 254;
+ team^.teamName[l + 1]:= #0;
+ listOfTeamNames[i]:= @team^.teamName[1];
+ inc(team)
+ end;
+
+ listOfTeamNames[t]:= nil;
+
+ getTeamsList:= listOfTeamNames
+end;
+
+function teamByName(s: shortstring): PTeam;
+var i: Longword;
+ team: PTeam;
+begin
+ team:= teamsList;
+ i:= 0;
+ while (i < teamsNumber) and (team^.teamName <> s) do
+ begin
+ inc(team);
+ inc(i)
+ end;
+
+ if i < teamsNumber then teamByName:= team else teamByName:= nil
+end;
+
+procedure freeTeamsList;
+begin
+ if teamsList <> nil then
+ FreeMem(teamsList, sizeof(teamsList^) * teamsNumber)
+end;
+
+procedure sendTeam(var team: TTeam);
+var i: Longword;
+begin
+ with team do
+ begin
+ sendNetLn('ADD_TEAM');
+ sendNetLn(teamName);
+ sendNetLn(IntToStr(color));
+ sendNetLn(grave);
+ sendNetLn(fort);
+ sendNetLn(voice);
+ sendNetLn(flag);
+ sendNetLn(IntToStr(botLevel));
+ for i := 0 to 7 do
+ begin
+ sendNetLn(hedgehogs[i].name);
+ sendNetLn(hedgehogs[i].hat);
+ end;
+ sendNetLn('')
+ end;
+end;
+
+procedure removeTeam(teamName: shortstring);
+begin
+ sendNetLn('REMOVE_TEAM');
+ sendNet(teamName)
+end;
+
+end.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hedgewars/uFLThemes.pas Sun Dec 06 20:36:21 2015 +0300
@@ -0,0 +1,85 @@
+unit uFLThemes;
+interface
+
+function getThemesList: PPChar; cdecl;
+procedure freeThemesList(list: PPChar); cdecl;
+function getThemeIcon(themeName: PChar; buffer: PChar; buflen: Longword): Longword; cdecl;
+
+const colorsSet: array[0..8] of shortstring = (
+ '16712196'
+ , '4817089'
+ , '1959610'
+ , '11878895'
+ , '10526880'
+ , '2146048'
+ , '16681742'
+ , '6239749'
+ , '16776961');
+
+implementation
+uses uPhysFSLayer;
+
+function getThemesList: PPChar; cdecl;
+var list, res, tmp: PPChar;
+ i, size: Longword;
+begin
+ list:= pfsEnumerateFiles('Themes');
+ size:= 0;
+ tmp:= list;
+ while tmp^ <> nil do
+ begin
+ inc(size);
+ inc(tmp)
+ end;
+
+ res:= GetMem((3 + size) * sizeof(PChar));
+ res^:= PChar(list);
+ inc(res);
+ res^:= PChar(res + size + 2);
+ inc(res);
+
+ getThemesList:= res;
+
+ for i:= 1 to size do
+ begin
+ if pfsExists('/Themes/' + shortstring(list^) + '/icon.png') then
+ begin
+ res^:= list^;
+ inc(res)
+ end;
+
+ inc(list)
+ end;
+
+ res^:= nil
+end;
+
+procedure freeThemesList(list: PPChar); cdecl;
+var listEnd: PPChar;
+begin
+ dec(list);
+ listEnd:= PPChar(list^);
+ dec(list);
+
+ pfsFreeList(PPChar(list^));
+ freeMem(list, (listEnd - list) * sizeof(PChar))
+end;
+
+function getThemeIcon(themeName: PChar; buffer: PChar; buflen: Longword): Longword; cdecl;
+var s: shortstring;
+ f: PFSFile;
+begin
+ s:= '/Themes/' + shortstring(themeName) + '/icon@2x.png';
+
+ f:= pfsOpenRead(s);
+
+ if f = nil then
+ getThemeIcon:= 0
+ else
+ begin
+ getThemeIcon:= pfsBlockRead(f, buffer, buflen);
+ pfsClose(f)
+ end;
+end;
+
+end.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hedgewars/uFLTypes.pas Sun Dec 06 20:36:21 2015 +0300
@@ -0,0 +1,125 @@
+unit uFLTypes;
+interface
+
+const
+ MAXARGS = 32;
+
+type
+ TMessageType = (mtRenderingPreview, mtPreview, mtAddPlayingTeam, mtRemovePlayingTeam
+ , mtAddTeam, mtRemoveTeam, mtTeamColor, mtHedgehogsNumber, mtNetData
+ , mtFlibEvent, mtConnected, mtDisconnected, mtAddLobbyClient
+ , mtRemoveLobbyClient, mtLobbyChatLine, mtAddRoomClient
+ , mtRemoveRoomClient, mtRoomChatLine, mtAddRoom, mtUpdateRoom
+ , mtRemoveRoom, mtError, mtWarning, mtMoveToLobby, mtMoveToRoom
+ , mtNickname, mtSeed, mtTheme, mtScript, mtFeatureSize, mtMapGen
+ , mtMap, mtMazeSize, mtTemplate, mtAmmo, mtScheme);
+
+ TFLIBEvent = (flibGameFinished);
+
+ TIPCMessage = record
+ str: shortstring;
+ len: Longword;
+ buf: Pointer
+ end;
+
+ TIPCCallback = procedure (p: pointer; msg: PChar; len: Longword);
+ TUICallback = procedure (p: pointer; msgType: TMessageType; msg: PChar; len: Longword); cdecl;
+
+ TGameType = (gtPreview, gtLocal);
+ THedgehog = record
+ name: shortstring;
+ hat: shortstring;
+ end;
+ TTeam = record
+ teamName
+ , flag
+ , grave
+ , fort
+ , voice
+ , owner: shortstring;
+ color: Longword;
+ extDriven: boolean;
+ botLevel: Longword;
+ hedgehogs: array[0..7] of THedgehog;
+ hogsNumber: Longword;
+ end;
+ PTeam = ^TTeam;
+
+ TScheme = record
+ schemeName
+ , scriptparam : shortstring;
+ fortsmode
+ , divteams
+ , solidland
+ , border
+ , lowgrav
+ , laser
+ , invulnerability
+ , mines
+ , vampiric
+ , karma
+ , artillery
+ , randomorder
+ , king
+ , placehog
+ , sharedammo
+ , disablegirders
+ , disablewind
+ , morewind
+ , tagteam
+ , resethealth
+ , disablelandobjects
+ , aisurvival
+ , infattack
+ , resetweps
+ , perhogammo
+ , bottomborder: boolean;
+ damagefactor
+ , turntime
+ , health
+ , suddendeath
+ , caseprobability
+ , minestime
+ , landadds
+ , minedudpct
+ , explosives
+ , minesnum
+ , healthprobability
+ , healthcaseamount
+ , waterrise
+ , healthdecrease
+ , ropepct
+ , getawaytime
+ , airmines
+ , worldedge: LongInt
+ end;
+ PScheme = ^TScheme;
+ TAmmo = record
+ ammoName: shortstring;
+ a, b, c, d: shortstring;
+ end;
+ PAmmo = ^TAmmo;
+
+ PGameConfig = ^TGameConfig;
+ TGameConfig = record
+ seed: shortstring;
+ theme: shortstring;
+ script: shortstring;
+ map: shortstring;
+ scheme: TScheme;
+ ammo: TAmmo;
+ mapgen: LongInt;
+ featureSize: LongInt;
+ mazesize: LongInt;
+ template: LongInt;
+ gameType: TGameType;
+ teams: array[0..7] of TTeam;
+ arguments: array[0..Pred(MAXARGS)] of shortstring;
+ argv: array[0..Pred(MAXARGS)] of PChar;
+ argumentsNumber: Longword;
+ nextConfig: PGameConfig;
+ end;
+
+implementation
+
+end.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hedgewars/uFLUICallback.pas Sun Dec 06 20:36:21 2015 +0300
@@ -0,0 +1,32 @@
+unit uFLUICallback;
+interface
+uses uFLTypes;
+
+procedure registerUIMessagesCallback(p: pointer; f: TUICallback); cdecl;
+procedure sendUI(msgType: TMessageType; msg: PChar; len: Longword);
+
+implementation
+uses uFLIPC;
+
+var uiCallbackPointer: pointer;
+ uiCallbackFunction: TUICallback;
+
+procedure engineMessageCallback(p: pointer; msg: PChar; len: Longword);
+begin
+ if len = 128 * 256 then uiCallbackFunction(uiCallbackPointer, mtPreview, msg, len)
+end;
+
+procedure registerUIMessagesCallback(p: pointer; f: TUICallback); cdecl;
+begin
+ uiCallbackPointer:= p;
+ uiCallbackFunction:= f;
+
+ registerIPCCallback(nil, @engineMessageCallback)
+end;
+
+procedure sendUI(msgType: TMessageType; msg: PChar; len: Longword);
+begin
+ uiCallbackFunction(uiCallbackPointer, msgType, msg, len)
+end;
+
+end.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hedgewars/uFLUtils.pas Sun Dec 06 20:36:21 2015 +0300
@@ -0,0 +1,80 @@
+unit uFLUtils;
+interface
+
+function str2PChar(const s: shortstring): PChar;
+function intToStr(n: LongInt): shortstring;
+function strToInt(s: shortstring): LongInt;
+function midStr(s: shortstring; pos: byte): shortstring;
+procedure underScore2Space(var s: shortstring);
+function readInt(name, input: shortstring; var value: LongInt): boolean;
+function readBool(name, input: shortstring; var value: boolean): boolean;
+
+implementation
+
+var
+ str2PCharBuffer: array[0..255] of char;
+
+function str2PChar(const s: shortstring): PChar;
+var i: Integer;
+begin
+ for i:= 1 to Length(s) do
+ begin
+ str2PCharBuffer[i - 1] := s[i];
+ end;
+ str2PCharBuffer[Length(s)]:= #0;
+ str2PChar:= @(str2PCharBuffer[0]);
+end;
+
+function intToStr(n: LongInt): shortstring;
+begin
+ str(n, intToStr)
+end;
+
+function strToInt(s: shortstring): LongInt;
+begin
+val(s, strToInt);
+end;
+
+function midStr(s: shortstring; pos: byte): shortstring;
+begin
+ midStr:= copy(s, pos, length(s) - pos + 1)
+end;
+
+procedure underScore2Space(var s: shortstring);
+var i: LongInt;
+begin
+ for i:= length(s) downto 1 do
+ if s[i] = '_' then s[i]:= ' '
+end;
+
+function readInt(name, input: shortstring; var value: LongInt): boolean;
+var l: LongInt;
+begin
+ name:= name + '=';
+ l:= length(name);
+
+ if copy(input, 1, l) = name then
+ begin
+ value:= strToInt(midStr(input, l + 1));
+ readInt:= true
+ end
+ else
+ readInt:= false
+end;
+
+function readBool(name, input: shortstring; var value: boolean): boolean;
+var l: LongInt;
+begin
+ name:= name + '=';
+ l:= length(name);
+
+ if copy(input, 1, l) = name then
+ begin
+ value:= (length(input) > l) and (input[l + 1] <> 'f');
+ readBool:= true
+ end
+ else
+ readBool:= false
+end;
+
+end.
--- a/hedgewars/uIO.pas Thu Nov 19 13:30:34 2015 +0300
+++ b/hedgewars/uIO.pas Sun Dec 06 20:36:21 2015 +0300
@@ -25,7 +25,6 @@
procedure initModule;
procedure freeModule;
-procedure InitIPC;
procedure SendIPC(s: shortstring);
procedure SendIPCXY(cmd: char; X, Y: LongInt);
procedure SendIPCRaw(p: pointer; len: Longword);
@@ -39,7 +38,7 @@
procedure doPut(putX, putY: LongInt; fromAI: boolean);
implementation
-uses uConsole, uConsts, uVariables, uCommands, uUtils, uDebug;
+uses uConsole, uConsts, uVariables, uCommands, uUtils, uDebug, uFLIPC;
const
cSendEmptyPacketTime = 1000;
@@ -55,11 +54,9 @@
2: (str: shortstring);
end;
-var IPCSock: PTCPSocket;
- fds: PSDLNet_SocketSet;
+var
isPonged: boolean;
- SocketString: shortstring;
-
+
headcmd: PCmd;
lastcmd: PCmd;
@@ -101,23 +98,6 @@
dispose(tmp)
end;
-procedure InitIPC;
-var ipaddr: TIPAddress;
-begin
- WriteToConsole('Init SDL_Net... ');
- SDLTry(SDLNet_Init = 0, 'SDLNet_Init', true);
- fds:= SDLNet_AllocSocketSet(1);
- SDLTry(fds <> nil, 'SDLNet_AllocSocketSet', true);
- WriteLnToConsole(msgOK);
- WriteToConsole('Establishing IPC connection to tcp 127.0.0.1:' + IntToStr(ipcPort) + ' ');
- {$HINTS OFF}
- SDLTry(SDLNet_ResolveHost(ipaddr, PChar('127.0.0.1'), ipcPort) = 0, 'SDLNet_ResolveHost', true);
- {$HINTS ON}
- IPCSock:= SDLNet_TCP_Open(ipaddr);
- SDLTry(IPCSock <> nil, 'SDLNet_TCP_Open', true);
- WriteLnToConsole(msgOK)
-end;
-
procedure ParseChatCommand(command: shortstring; message: shortstring;
messageStartIndex: Byte);
var
@@ -176,31 +156,9 @@
end;
procedure IPCCheckSock;
-var i: LongInt;
- s: shortstring;
begin
- if IPCSock = nil then
- exit;
-
- fds^.numsockets:= 0;
- SDLNet_AddSocket(fds, IPCSock);
-
- while SDLNet_CheckSockets(fds, 0) > 0 do
- begin
- i:= SDLNet_TCP_Recv(IPCSock, @s[1], 255 - Length(SocketString));
- if i > 0 then
- begin
- s[0]:= char(i);
- SocketString:= SocketString + s;
- while (Length(SocketString) > 1) and (Length(SocketString) > byte(SocketString[1])) do
- begin
- ParseIPCCommand(copy(SocketString, 2, byte(SocketString[1])));
- Delete(SocketString, 1, Succ(byte(SocketString[1])))
- end
- end
- else
- OutError('IPC connection lost', true)
- end;
+ while ipcCheckFromFrontend() do
+ ParseIPCCommand(ipcReadFromFrontend())
end;
procedure LoadRecordFromFile(fileName: shortstring);
@@ -260,18 +218,11 @@
procedure flushBuffer();
begin
- if IPCSock <> nil then
- begin
- SDLNet_TCP_Send(IPCSock, @sendBuffer.buf, sendBuffer.count);
- flushDelayTicks:= 0;
- sendBuffer.count:= 0
- end
+
end;
procedure SendIPC(s: shortstring);
begin
-if IPCSock <> nil then
- begin
if s[0] > #251 then
s[0]:= #251;
@@ -280,37 +231,22 @@
AddFileLog('[IPC out] '+ sanitizeCharForLog(s[1]));
inc(s[0], 2);
- if isSyncedCommand(s[1]) then
- begin
- if sendBuffer.count + byte(s[0]) >= cSendBufferSize then
- flushBuffer();
-
- Move(s, sendBuffer.buf[sendBuffer.count], byte(s[0]) + 1);
- inc(sendBuffer.count, byte(s[0]) + 1);
-
- if (s[1] = 'N') or (s[1] = '#') then
- flushBuffer();
- end else
- SDLNet_TCP_Send(IPCSock, @s, Succ(byte(s[0])))
- end
+ ipcToFrontend(s)
end;
procedure SendIPCRaw(p: pointer; len: Longword);
begin
-if IPCSock <> nil then
- begin
- SDLNet_TCP_Send(IPCSock, p, len)
- end
+ ipcToFrontendRaw(p, len)
end;
procedure SendIPCXY(cmd: char; X, Y: LongInt);
var s: shortstring;
begin
-s[0]:= #9;
-s[1]:= cmd;
-SDLNet_Write32(X, @s[2]);
-SDLNet_Write32(Y, @s[6]);
-SendIPC(s)
+ s[0]:= #9;
+ s[1]:= cmd;
+ SDLNet_Write32(X, @s[2]);
+ SDLNet_Write32(Y, @s[6]);
+ SendIPC(s)
end;
procedure IPCWaitPongEvent;
@@ -444,13 +380,13 @@
procedure chFatalError(var s: shortstring);
begin
SendIPC('E' + s);
- // TODO: should we try to clean more stuff here?
+{ // TODO: should we try to clean more stuff here?
SDL_Quit;
if IPCSock <> nil then
halt(HaltFatalError)
else
- halt(HaltFatalErrorNoIPC);
+ halt(HaltFatalErrorNoIPC);}
end;
procedure doPut(putX, putY: LongInt; fromAI: boolean);
@@ -502,12 +438,9 @@
begin
RegisterVariable('fatal', @chFatalError, true );
- IPCSock:= nil;
-
headcmd:= nil;
lastcmd:= nil;
isPonged:= false;
- SocketString:= '';
hiTicks:= 0;
flushDelayTicks:= 0;
@@ -517,10 +450,6 @@
procedure freeModule;
begin
while headcmd <> nil do RemoveCmd;
- SDLNet_FreeSocketSet(fds);
- SDLNet_TCP_Close(IPCSock);
- SDLNet_Quit();
-
end;
end.
--- a/hedgewars/uLocale.pas Thu Nov 19 13:30:34 2015 +0300
+++ b/hedgewars/uLocale.pas Sun Dec 06 20:36:21 2015 +0300
@@ -135,17 +135,18 @@
{$IFDEF HWLIBRARY}
procedure LoadLocaleWrapper(path: pchar; filename: pchar); cdecl; export;
begin
- PathPrefix := Strpas(path);
+// FIXME
+{ PathPrefix := Strpas(path);
uUtils.initModule(false);
uVariables.initModule;
uPhysFSLayer.initModule;
-
+}
LoadLocale(Strpas(filename));
-
+{
uPhysFSLayer.freeModule;
uVariables.freeModule;
- uUtils.freeModule;
+ uUtils.freeModule;}
end;
{$ENDIF}
--- a/hedgewars/uMisc.pas Thu Nov 19 13:30:34 2015 +0300
+++ b/hedgewars/uMisc.pas Sun Dec 06 20:36:21 2015 +0300
@@ -280,11 +280,12 @@
// allocate and fill structure that will be passed to new thread
New(image); // will be disposed in SaveScreenshot()
-if dump = 2 then
+{if dump = 2 then
image^.filename:= shortstring(UserPathPrefix) + filename + '_landpixels' + ext
else if dump = 1 then
image^.filename:= shortstring(UserPathPrefix) + filename + '_land' + ext
-else image^.filename:= shortstring(UserPathPrefix) + filename + ext;
+else image^.filename:= shortstring(UserPathPrefix) + filename + ext;}
+image^.filename:= filename + ext;
if dump <> 0 then
begin
--- a/hedgewars/uPhysFSLayer.pas Thu Nov 19 13:30:34 2015 +0300
+++ b/hedgewars/uPhysFSLayer.pas Sun Dec 06 20:36:21 2015 +0300
@@ -13,7 +13,7 @@
{$linklib physlayer}
{$ENDIF}
-procedure initModule;
+procedure initModule(localPrefix, userPrefix: PChar);
procedure freeModule;
type PFSFile = pointer;
@@ -23,11 +23,14 @@
function pfsOpenRead(fname: shortstring): PFSFile;
function pfsClose(f: PFSFile): boolean;
+function pfsSeek(f: PFSFile; pos: QWord): boolean;
procedure pfsReadLn(f: PFSFile; var s: shortstring);
procedure pfsReadLnA(f: PFSFile; var s: ansistring);
function pfsBlockRead(f: PFSFile; buf: pointer; size: Int64): Int64;
function pfsEOF(f: PFSFile): boolean;
+function pfsEnumerateFiles(dir: shortstring): PPChar;
+procedure pfsFreeList(list: PPChar);
function pfsExists(fname: shortstring): boolean;
@@ -48,9 +51,12 @@
function PHYSFS_openRead(fname: PChar): PFSFile; cdecl; external PhysfsLibName;
function PHYSFS_eof(f: PFSFile): LongBool; cdecl; external PhysfsLibName;
function PHYSFS_readBytes(f: PFSFile; buffer: pointer; len: Int64): Int64; cdecl; external PhysfsLibName;
+function PHYSFS_seek(f: PFSFile; pos: QWord): LongBool; cdecl; external PhysfsLibName;
function PHYSFS_close(f: PFSFile): LongBool; cdecl; external PhysfsLibName;
function PHYSFS_exists(fname: PChar): LongBool; cdecl; external PhysfsLibName;
function PHYSFS_getLastError(): PChar; cdecl; external PhysfsLibName;
+function PHYSFS_enumerateFiles(dir: PChar): PPChar; cdecl; external PhysfsLibName;
+procedure PHYSFS_freeList(list: PPChar); cdecl; external PhysfsLibName;
{$ELSE}
function PHYSFS_readBytes(f: PFSFile; buffer: pointer; len: Int64): Int64;
begin
@@ -83,11 +89,25 @@
exit(PHYSFS_close(f))
end;
+function pfsSeek(f: PFSFile; pos: QWord): boolean;
+begin
+ exit(PHYSFS_seek(f, 0));
+end;
+
function pfsExists(fname: shortstring): boolean;
begin
exit(PHYSFS_exists(Str2PChar(fname)))
end;
+function pfsEnumerateFiles(dir: shortstring): PPChar;
+begin
+ exit(PHYSFS_enumerateFiles(Str2PChar(dir)))
+end;
+
+procedure pfsFreeList(list: PPChar);
+begin
+ PHYSFS_freeList(list)
+end;
procedure pfsReadLn(f: PFSFile; var s: shortstring);
var c: char;
@@ -138,9 +158,9 @@
procedure pfsMount(path: ansistring; mountpoint: PChar);
begin
if PHYSFS_mount(PChar(path), mountpoint, false) then
- AddFileLog('[PhysFS] mount ' + shortstring(path) + ' at ' + shortstring(mountpoint) + ' : ok')
+ //AddFileLog('[PhysFS] mount ' + shortstring(path) + ' at ' + shortstring(mountpoint) + ' : ok')
else
- AddFileLog('[PhysFS] mount ' + shortstring(path) + ' at ' + shortstring(mountpoint) + ' : FAILED ("' + shortstring(PHYSFS_getLastError()) + '")');
+ //AddFileLog('[PhysFS] mount ' + shortstring(path) + ' at ' + shortstring(mountpoint) + ' : FAILED ("' + shortstring(PHYSFS_getLastError()) + '")');
end;
procedure pfsMountAtRoot(path: ansistring);
@@ -148,22 +168,18 @@
pfsMount(path, PChar(_S'/'));
end;
-procedure initModule;
+procedure initModule(localPrefix, userPrefix: PChar);
var i: LongInt;
cPhysfsId: shortstring;
{$IFNDEF MOBILE}
fp: PChar;
{$ENDIF}
begin
-{$IFDEF HWLIBRARY}
//TODO: http://icculus.org/pipermail/physfs/2011-August/001006.html
cPhysfsId:= GetCurrentDir() + {$IFDEF DARWIN}{$IFNDEF IPHONEOS}'/Hedgewars.app/Contents/MacOS/' + {$ENDIF}{$ENDIF} ' hedgewars';
-{$ELSE}
- cPhysfsId:= ParamStr(0);
-{$ENDIF}
i:= PHYSFS_init(Str2PChar(cPhysfsId));
- AddFileLog('[PhysFS] init: ' + inttostr(i));
+ //AddFileLog('[PhysFS] init: ' + inttostr(i));
{$IFNDEF MOBILE}
// mount system fonts paths first
@@ -175,14 +191,12 @@
end;
{$ENDIF}
- pfsMountAtRoot(PathPrefix);
- pfsMountAtRoot(UserPathPrefix + ansistring('/Data'));
+ pfsMountAtRoot(localPrefix);
+ pfsMountAtRoot(userPrefix + ansistring('/Data'));
+ pfsMount(userPrefix, PChar('/Config'));
hedgewarsMountPackages;
- // need access to teams and frontend configs (for bindings)
- pfsMountAtRoot(UserPathPrefix);
-
if cTestLua then
begin
pfsMountAtRoot(ansistring(ExtractFileDir(cScriptName)));
--- a/hedgewars/uTypes.pas Thu Nov 19 13:30:34 2015 +0300
+++ b/hedgewars/uTypes.pas Sun Dec 06 20:36:21 2015 +0300
@@ -45,7 +45,7 @@
TPathType = (ptNone, ptData, ptGraphics, ptThemes, ptCurrTheme, ptTeams, ptMaps,
ptMapCurrent, ptDemos, ptSounds, ptGraves, ptFonts, ptForts, ptLocale,
ptAmmoMenu, ptHedgehog, ptVoices, ptHats, ptFlags, ptMissionMaps,
- ptSuddenDeath, ptButtons, ptShaders);
+ ptSuddenDeath, ptButtons, ptShaders, ptConfig);
// Available sprites for displaying stuff
TSprite = (sprWater, sprCloud, sprBomb, sprBigDigit, sprFrame,
--- a/hedgewars/uUtils.pas Thu Nov 19 13:30:34 2015 +0300
+++ b/hedgewars/uUtils.pas Sun Dec 06 20:36:21 2015 +0300
@@ -329,7 +329,7 @@
function Str2PChar(const s: shortstring): PChar;
-var i :Integer ;
+var i: Integer;
begin
for i:= 1 to Length(s) do
begin
@@ -547,7 +547,7 @@
{$ENDIF}
{$I-}
rwfailed:= false;
- if (length(UserPathPrefix) > 0) then
+ (*if (length(UserPathPrefix) > 0) then
begin
{$IFNDEF PAS2C}
// create directory if it doesn't exist
@@ -567,10 +567,10 @@
inc(i)
end;
end;
-
+ *)
{$IFNDEF PAS2C}
// if everything fails, write to stderr
- if (length(UserPathPrefix) = 0) or (rwfailed) then
+ //if (length(UserPathPrefix) = 0) or (rwfailed) then
logFile:= stderr;
{$ENDIF}
{$I+}
--- a/hedgewars/uVariables.pas Thu Nov 19 13:30:34 2015 +0300
+++ b/hedgewars/uVariables.pas Sun Dec 06 20:36:21 2015 +0300
@@ -36,14 +36,11 @@
cNewScreenWidth : LongInt;
cNewScreenHeight : LongInt;
cScreenResizeDelay : LongWord;
- ipcPort : Word;
AprilOne : boolean;
cFullScreen : boolean;
cLocaleFName : shortstring;
cLocale : shortstring;
cTimerInterval : LongInt;
- PathPrefix : ansistring;
- UserPathPrefix : ansistring;
cShowFPS : boolean;
cFlattenFlakes : boolean;
cFlattenClouds : boolean;
@@ -262,11 +259,11 @@
const
cPathzInit: array[TPathType] of shortstring = (
'', // ptNone
- '//', // ptData
+ '/', // ptData
'/Graphics', // ptGraphics
'/Themes', // ptThemes
'/Themes/Bamboo', // ptCurrTheme
- '/Teams', // ptTeams
+ '/Config/Teams', // ptTeams
'/Maps', // ptMaps
'', // ptMapCurrent
'/Demos', // ptDemos
@@ -283,7 +280,8 @@
'/Missions/Maps', // ptMissionMaps
'/Graphics/SuddenDeath', // ptSuddenDeath
'/Graphics/Buttons', // ptButton
- '/Shaders' // ptShaders
+ '/Shaders', // ptShaders
+ '/Config' // ptConfig
);
var
@@ -2484,13 +2482,10 @@
cLocaleFName := 'en.txt';
cFullScreen := false;
- UserPathPrefix := '';
- ipcPort := 0;
recordFileName := '';
UserNick := '';
cStereoMode := smNone;
GrayScale := false;
- PathPrefix := './';
GameType := gmtLocal;
cOnlyStats := False;
cScriptName := '';
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/OfficialChallenges/racer_#17.hwmap Sun Dec 06 20:36:21 2015 +0300
@@ -0,0 +1,1 @@
+AAAA4Xic43vHWMfAc4SFi4FrB5DFrsEiy8DCyriegTGGRYeBIYldlIGfkX02A/di9qIO7sWsrxhARMf/K0AWLy9bUwffXiDBEst2qPP/b3bRzQwO7FIXGO3YpUAGmC0GGrVlMbsui9JiEPcSkLgDMn7LJRZWpkwGoIT6JSDxn4HvCWPvZq7NjHWbQc64wX2FSYmB7yVj8w0+VqZ0Bp6TLAybgY5kvcFzhOk0AwMDu+gmkNMuMFYC3ceSw3bhApM7u0AXgwgjbyvDJwZ9Bj5p9tBW/nvsoQwAuyw7Aw==
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/qmlFrontend/CMakeLists.txt Sun Dec 06 20:36:21 2015 +0300
@@ -0,0 +1,27 @@
+cmake_minimum_required(VERSION 2.8.11)
+
+project(hedgewars)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+set(CMAKE_AUTOMOC ON)
+
+find_package(OpenGL)
+
+find_package(Qt5 COMPONENTS Core Qml Quick Gui)
+
+qt5_add_resources(qresources qmlFrontend.qrc)
+
+add_executable(hedgewars WIN32
+ main
+ hwengine
+ previewimageprovider
+ themeiconprovider
+ qtquick2applicationviewer/qtquick2applicationviewer
+ flib.h
+ ${qresources}
+ )
+
+include_directories(${OPENGL_INCLUDE_DIR})
+
+target_link_libraries(hedgewars Qt5::Core Qt5::Gui Qt5::Quick Qt5::Qml)
+install(PROGRAMS "${EXECUTABLE_OUTPUT_PATH}/hedgewars${CMAKE_EXECUTABLE_SUFFIX}" DESTINATION ${target_binary_install_dir})
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/qmlFrontend/flib.h Sun Dec 06 20:36:21 2015 +0300
@@ -0,0 +1,99 @@
+#ifndef FLIB_H
+#define FLIB_H
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum MessageType {
+ MSG_RENDERINGPREVIEW
+ , MSG_PREVIEW
+ , MSG_ADDPLAYINGTEAM
+ , MSG_REMOVEPLAYINGTEAM
+ , MSG_ADDTEAM
+ , MSG_REMOVETEAM
+ , MSG_TEAMCOLOR
+ , MSG_HEDGEHOGSNUMBER
+ , MSG_NETDATA
+ , MSG_FLIBEVENT
+ , MSG_CONNECTED
+ , MSG_DISCONNECTED
+ , MSG_ADDLOBBYCLIENT
+ , MSG_REMOVELOBBYCLIENT
+ , MSG_LOBBYCHATLINE
+ , MSG_ADDROOMCLIENT
+ , MSG_REMOVEROOMCLIENT
+ , MSG_ROOMCHATLINE
+ , MSG_ADDROOM
+ , MSG_UPDATEROOM
+ , MSG_REMOVEROOM
+ , MSG_ERROR
+ , MSG_WARNING
+ , MSG_MOVETOLOBBY
+ , MSG_MOVETOROOM
+ , MSG_NICKNAME
+ , MSG_SEED
+ , MSG_THEME
+ , MSG_SCRIPT
+ , MSG_FEATURESIZE
+ , MSG_MAPGEN
+ , MSG_MAP
+ , MSG_MAZESIZE
+ , MSG_TEMPLATE
+ , MSG_AMMO
+ , MSG_SCHEME
+};
+
+typedef union string255_
+ {
+ struct {
+ unsigned char s[256];
+ };
+ struct {
+ unsigned char len;
+ unsigned char str[255];
+ };
+ } string255;
+
+typedef void RunEngine_t(int argc, const char ** argv);
+typedef void registerUIMessagesCallback_t(void * context, void (*)(void * context, MessageType mt, const char * msg, uint32_t len));
+typedef void getPreview_t();
+typedef void runQuickGame_t();
+typedef void runLocalGame_t();
+typedef void resetGameConfig_t();
+typedef void setSeed_t(const char * seed);
+typedef char *getSeed_t();
+typedef void setTheme_t(const char * themeName);
+typedef void setScript_t(const char * scriptName);
+typedef void setScheme_t(const char * schemeName);
+typedef void setAmmo_t(const char * ammoName);
+typedef void flibInit_t(const char * localPrefix, const char * userPrefix);
+typedef void flibFree_t();
+typedef void passNetData_t(const char * data);
+typedef void passFlibEvent_t(const char * data);
+typedef void sendChatLine_t(const char * msg);
+typedef void joinRoom_t(const char * roomName);
+typedef void partRoom_t(const char * message);
+
+typedef char **getThemesList_t();
+typedef void freeThemesList_t(char **list);
+typedef uint32_t getThemeIcon_t(char * theme, char * buffer, uint32_t size);
+
+typedef char **getScriptsList_t();
+typedef char **getSchemesList_t();
+typedef char **getAmmosList_t();
+
+typedef char **getTeamsList_t();
+typedef void tryAddTeam_t(const char * teamName);
+typedef void tryRemoveTeam_t(const char * teamName);
+typedef void changeTeamColor_t(const char * teamName, int32_t dir);
+
+typedef void connectOfficialServer_t();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // FLIB_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/qmlFrontend/hwengine.cpp Sun Dec 06 20:36:21 2015 +0300
@@ -0,0 +1,432 @@
+#include <QLibrary>
+#include <QtQml>
+#include <QDebug>
+#include <QPainter>
+#include <QUuid>
+
+#include "hwengine.h"
+#include "previewimageprovider.h"
+#include "themeiconprovider.h"
+
+extern "C" {
+ RunEngine_t *flibRunEngine;
+ registerUIMessagesCallback_t *flibRegisterUIMessagesCallback;
+ setSeed_t *flibSetSeed;
+ getSeed_t *flibGetSeed;
+ setTheme_t *flibSetTheme;
+ setScript_t *flibSetScript;
+ setScheme_t *flibSetScheme;
+ setAmmo_t *flibSetAmmo;
+ getPreview_t *flibGetPreview;
+ runQuickGame_t *flibRunQuickGame;
+ runLocalGame_t *flibRunLocalGame;
+ flibInit_t *flibInit;
+ flibFree_t *flibFree;
+ resetGameConfig_t * flibResetGameConfig;
+ getThemesList_t *flibGetThemesList;
+ freeThemesList_t *flibFreeThemesList;
+ getThemeIcon_t *flibGetThemeIcon;
+ getScriptsList_t *flibGetScriptsList;
+ getSchemesList_t *flibGetSchemesList;
+ getAmmosList_t *flibGetAmmosList;
+ getTeamsList_t *flibGetTeamsList;
+ tryAddTeam_t * flibTryAddTeam;
+ tryRemoveTeam_t * flibTryRemoveTeam;
+ changeTeamColor_t * flibChangeTeamColor;
+
+ connectOfficialServer_t * flibConnectOfficialServer;
+ passNetData_t * flibPassNetData;
+ passFlibEvent_t * flibPassFlibEvent;
+ sendChatLine_t * flibSendChatLine;
+ joinRoom_t * flibJoinRoom;
+ partRoom_t * flibPartRoom;
+}
+
+Q_DECLARE_METATYPE(MessageType)
+
+HWEngine::HWEngine(QQmlEngine *engine, QObject *parent) :
+ QObject(parent),
+ m_engine(engine)
+{
+ qRegisterMetaType<MessageType>("MessageType");
+
+#ifdef Q_OS_WIN
+ QLibrary hwlib("./libhwengine.dll");
+#else
+ QLibrary hwlib("./libhwengine.so");
+#endif
+
+ if(!hwlib.load())
+ qWarning() << "Engine library not found" << hwlib.errorString();
+
+ flibRunEngine = (RunEngine_t*) hwlib.resolve("RunEngine");
+ flibRegisterUIMessagesCallback = (registerUIMessagesCallback_t*) hwlib.resolve("registerUIMessagesCallback");
+ flibGetSeed = (getSeed_t*) hwlib.resolve("getSeed");
+ flibGetPreview = (getPreview_t*) hwlib.resolve("getPreview");
+ flibRunQuickGame = (runQuickGame_t*) hwlib.resolve("runQuickGame");
+ flibRunLocalGame = (runLocalGame_t*) hwlib.resolve("runLocalGame");
+ flibInit = (flibInit_t*) hwlib.resolve("flibInit");
+ flibFree = (flibFree_t*) hwlib.resolve("flibFree");
+
+ flibSetSeed = (setSeed_t*) hwlib.resolve("setSeed");
+ flibSetTheme = (setTheme_t*) hwlib.resolve("setTheme");
+ flibSetScript = (setScript_t*) hwlib.resolve("setScript");
+ flibSetScheme = (setScheme_t*) hwlib.resolve("setScheme");
+ flibSetAmmo = (setAmmo_t*) hwlib.resolve("setAmmo");
+
+ flibGetThemesList = (getThemesList_t*) hwlib.resolve("getThemesList");
+ flibFreeThemesList = (freeThemesList_t*) hwlib.resolve("freeThemesList");
+ flibGetThemeIcon = (getThemeIcon_t*) hwlib.resolve("getThemeIcon");
+
+ flibGetScriptsList = (getScriptsList_t*) hwlib.resolve("getScriptsList");
+ flibGetSchemesList = (getSchemesList_t*) hwlib.resolve("getSchemesList");
+ flibGetAmmosList = (getAmmosList_t*) hwlib.resolve("getAmmosList");
+
+ flibResetGameConfig = (resetGameConfig_t*) hwlib.resolve("resetGameConfig");
+ flibGetTeamsList = (getTeamsList_t*) hwlib.resolve("getTeamsList");
+ flibTryAddTeam = (tryAddTeam_t*) hwlib.resolve("tryAddTeam");
+ flibTryRemoveTeam = (tryRemoveTeam_t*) hwlib.resolve("tryRemoveTeam");
+ flibChangeTeamColor = (changeTeamColor_t*) hwlib.resolve("changeTeamColor");
+
+ flibConnectOfficialServer = (connectOfficialServer_t*) hwlib.resolve("connectOfficialServer");
+ flibPassNetData = (passNetData_t*) hwlib.resolve("passNetData");
+ flibPassFlibEvent = (passFlibEvent_t*) hwlib.resolve("passFlibEvent");
+ flibSendChatLine = (sendChatLine_t*) hwlib.resolve("sendChatLine");
+ flibJoinRoom = (joinRoom_t*) hwlib.resolve("joinRoom");
+ flibPartRoom = (partRoom_t*) hwlib.resolve("partRoom");
+
+ flibInit("/usr/home/unC0Rr/Sources/Hedgewars/Hedgewars-GC/share/hedgewars/Data", "/usr/home/unC0Rr/.hedgewars");
+ flibRegisterUIMessagesCallback(this, &guiMessagesCallback);
+
+ ThemeIconProvider * themeIcon = (ThemeIconProvider *)m_engine->imageProvider(QLatin1String("theme"));
+ themeIcon->setFileContentsFunction(flibGetThemeIcon);
+
+ fillModels();
+}
+
+HWEngine::~HWEngine()
+{
+ flibFree();
+}
+
+void HWEngine::getPreview()
+{
+ flibSetSeed(QUuid::createUuid().toString().toLatin1());
+ flibGetPreview();
+}
+
+void HWEngine::runQuickGame()
+{
+ flibSetSeed(QUuid::createUuid().toString().toLatin1());
+ flibRunQuickGame();
+}
+
+void HWEngine::runLocalGame()
+{
+ flibRunLocalGame();
+}
+
+
+static QObject *hwengine_singletontype_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
+{
+ Q_UNUSED(scriptEngine)
+
+ HWEngine *hwengine = new HWEngine(engine);
+ return hwengine;
+}
+
+void HWEngine::exposeToQML()
+{
+ qDebug("HWEngine::exposeToQML");
+ qmlRegisterSingletonType<HWEngine>("Hedgewars.Engine", 1, 0, "HWEngine", hwengine_singletontype_provider);
+}
+
+
+void HWEngine::guiMessagesCallback(void *context, MessageType mt, const char * msg, uint32_t len)
+{
+ HWEngine * obj = (HWEngine *)context;
+ QByteArray b = QByteArray(msg, len);
+
+ //qDebug() << "FLIPC in" << mt << " size = " << b.size();
+
+ QMetaObject::invokeMethod(obj, "engineMessageHandler", Qt::QueuedConnection, Q_ARG(MessageType, mt), Q_ARG(QByteArray, b));
+}
+
+void HWEngine::engineMessageHandler(MessageType mt, const QByteArray &msg)
+{
+ switch(mt)
+ {
+ case MSG_RENDERINGPREVIEW: {
+ emit previewIsRendering();
+ break;
+ }
+ case MSG_PREVIEW: {
+ PreviewImageProvider * preview = (PreviewImageProvider *)m_engine->imageProvider(QLatin1String("preview"));
+ preview->setPixmap(msg);
+ emit previewImageChanged();
+ break;
+ }
+ case MSG_ADDPLAYINGTEAM: {
+ QStringList l = QString::fromUtf8(msg).split('\n');
+ emit playingTeamAdded(l[1], l[0].toInt(), true);
+ break;
+ }
+ case MSG_REMOVEPLAYINGTEAM: {
+ emit playingTeamRemoved(QString::fromUtf8(msg));
+ break;
+ }
+ case MSG_ADDTEAM: {
+ emit localTeamAdded(QString::fromUtf8(msg), 0);
+ break;
+ }
+ case MSG_REMOVETEAM: {
+ emit localTeamRemoved(QString::fromUtf8(msg));
+ break;
+ }
+ case MSG_TEAMCOLOR: {
+ QStringList l = QString::fromUtf8(msg).split('\n');
+ emit teamColorChanged(l[0], QColor::fromRgba(l[1].toInt()).name());
+ break;
+ }
+ case MSG_HEDGEHOGSNUMBER: {
+ QStringList l = QString::fromUtf8(msg).split('\n');
+ emit hedgehogsNumberChanged(l[0], l[1].toInt());
+ break;
+ }
+ case MSG_NETDATA: {
+ flibPassNetData(msg.constData());
+ break;
+ }
+ case MSG_FLIBEVENT: {
+ flibPassFlibEvent(msg.constData());
+ break;
+ }
+ case MSG_CONNECTED: {
+ emit netConnected();
+ break;
+ }
+ case MSG_DISCONNECTED: {
+ emit netDisconnected(QString::fromUtf8(msg));
+ break;
+ }
+ case MSG_ADDLOBBYCLIENT: {
+ emit lobbyClientAdded(QString::fromUtf8(msg));
+ break;
+ }
+ case MSG_REMOVELOBBYCLIENT: {
+ QStringList l = QString::fromUtf8(msg).split('\n');
+ if(l.size() < 2)
+ l.append("");
+ emit lobbyClientRemoved(l[0], l[1]);
+ break;
+ }
+ case MSG_LOBBYCHATLINE: {
+ QStringList l = QString::fromUtf8(msg).split('\n');
+ emit lobbyChatLine(l[0], l[1]);
+ break;
+ }
+ case MSG_ADDROOMCLIENT: {
+ emit roomClientAdded(QString::fromUtf8(msg));
+ break;
+ }
+ case MSG_REMOVEROOMCLIENT: {
+ QStringList l = QString::fromUtf8(msg).split('\n');
+ if(l.size() < 2)
+ l.append("");
+ emit roomClientRemoved(l[0], l[1]);
+ break;
+ }
+ case MSG_ROOMCHATLINE: {
+ QStringList l = QString::fromUtf8(msg).split('\n');
+ emit roomChatLine(l[0], l[1]);
+ break;
+ }
+ case MSG_ADDROOM: {
+ QStringList l = QString::fromUtf8(msg).split('\n');
+ emit roomAdded(0, l[1], l[2].toInt(), l[3].toInt(), l[4], l[5], l[6], l[7], l[8]);
+ break;
+ }
+ case MSG_UPDATEROOM: {
+ QStringList l = QString::fromUtf8(msg).split('\n');
+ emit roomUpdated(l[0], 0, l[2], l[3].toInt(), l[4].toInt(), l[5], l[6], l[7], l[8], l[9]);
+ break;
+ }
+ case MSG_REMOVEROOM: {
+ emit roomRemoved(QString::fromUtf8(msg));
+ break;
+ }
+ case MSG_ERROR: {
+ emit errorMessage(QString::fromUtf8(msg));
+ break;
+ }
+ case MSG_WARNING: {
+ emit warningMessage(QString::fromUtf8(msg));
+ break;
+ }
+ case MSG_MOVETOLOBBY: {
+ emit movedToLobby();
+ break;
+ }
+ case MSG_MOVETOROOM: {
+ emit movedToRoom();
+ break;
+ }
+ case MSG_NICKNAME: {
+ m_myNickname = QString::fromUtf8(msg);
+ break;
+ }
+ case MSG_SEED: {
+ emit seedChanged(QString::fromUtf8(msg));
+ break;
+ }
+ case MSG_THEME: {
+ emit themeChanged(QString::fromUtf8(msg));
+ break;
+ }
+ case MSG_SCRIPT: {
+ emit scriptChanged(QString::fromUtf8(msg));
+ break;
+ }
+ case MSG_FEATURESIZE: {
+ emit featureSizeChanged(msg.toInt());
+ break;
+ }
+ case MSG_MAPGEN: {
+ emit mapGenChanged(msg.toInt());
+ break;
+ }
+ case MSG_MAP: {
+ emit mapChanged(QString::fromUtf8(msg));
+ break;
+ }
+ case MSG_MAZESIZE: {
+ emit mazeSizeChanged(msg.toInt());
+ break;
+ }
+ case MSG_TEMPLATE: {
+ emit templateChanged(msg.toInt());
+ break;
+ }
+ case MSG_AMMO: {
+ emit ammoChanged(QString::fromUtf8(msg));
+ break;
+ }
+ case MSG_SCHEME: {
+ emit schemeChanged(QString::fromUtf8(msg));
+ break;
+ }
+ }
+}
+
+QString HWEngine::currentSeed()
+{
+ return QString::fromLatin1(flibGetSeed());
+}
+
+void HWEngine::fillModels()
+{
+ QStringList resultModel;
+
+ char ** themes = flibGetThemesList();
+ for (char **i = themes; *i != NULL; i++)
+ resultModel << QString::fromUtf8(*i);
+ flibFreeThemesList(themes);
+
+ m_engine->rootContext()->setContextProperty("themesModel", QVariant::fromValue(resultModel));
+
+ // scripts model
+ resultModel.clear();
+ for (char **i = flibGetScriptsList(); *i != NULL; i++)
+ resultModel << QString::fromUtf8(*i);
+
+ m_engine->rootContext()->setContextProperty("scriptsModel", QVariant::fromValue(resultModel));
+
+ // schemes model
+ resultModel.clear();
+ for (char **i = flibGetSchemesList(); *i != NULL; i++)
+ resultModel << QString::fromUtf8(*i);
+
+ m_engine->rootContext()->setContextProperty("schemesModel", QVariant::fromValue(resultModel));
+
+ // ammos model
+ resultModel.clear();
+ for (char **i = flibGetAmmosList(); *i != NULL; i++)
+ resultModel << QString::fromUtf8(*i);
+
+ m_engine->rootContext()->setContextProperty("ammosModel", QVariant::fromValue(resultModel));
+}
+
+void HWEngine::getTeamsList()
+{
+ char ** teams = flibGetTeamsList();
+ for (char **i = teams; *i != NULL; i++) {
+ QString team = QString::fromUtf8(*i);
+
+ emit localTeamAdded(team, 0);
+ }
+}
+
+void HWEngine::tryAddTeam(const QString &teamName)
+{
+ flibTryAddTeam(teamName.toUtf8().constData());
+}
+
+void HWEngine::tryRemoveTeam(const QString &teamName)
+{
+ flibTryRemoveTeam(teamName.toUtf8().constData());
+}
+
+void HWEngine::resetGameConfig()
+{
+ flibResetGameConfig();
+}
+
+void HWEngine::changeTeamColor(const QString &teamName, int dir)
+{
+ flibChangeTeamColor(teamName.toUtf8().constData(), dir);
+}
+
+void HWEngine::connect(const QString &host, quint16 port)
+{
+ flibConnectOfficialServer();
+}
+
+void HWEngine::sendChatMessage(const QString &msg)
+{
+ flibSendChatLine(msg.toUtf8().constData());
+}
+
+void HWEngine::joinRoom(const QString &roomName)
+{
+ flibJoinRoom(roomName.toUtf8().constData());
+}
+
+void HWEngine::partRoom(const QString &message)
+{
+ flibPartRoom(message.toUtf8().constData());
+}
+
+QString HWEngine::myNickname()
+{
+ return m_myNickname;
+}
+
+void HWEngine::setTheme(const QString &theme)
+{
+ flibSetTheme(theme.toUtf8().constData());
+}
+
+void HWEngine::setScript(const QString &script)
+{
+ flibSetScript(script.toUtf8().constData());
+}
+
+void HWEngine::setScheme(const QString &scheme)
+{
+ flibSetScheme(scheme.toUtf8().constData());
+}
+
+void HWEngine::setAmmo(const QString &ammo)
+{
+ flibSetAmmo(ammo.toUtf8().constData());
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/qmlFrontend/hwengine.h Sun Dec 06 20:36:21 2015 +0300
@@ -0,0 +1,121 @@
+#ifndef HWENGINE_H
+#define HWENGINE_H
+
+#include <QObject>
+#include <QByteArray>
+#include <QVector>
+#include <QPixmap>
+
+#include "flib.h"
+
+class QQmlEngine;
+
+class HWEngine : public QObject
+{
+ Q_OBJECT
+public:
+ explicit HWEngine(QQmlEngine * engine, QObject *parent = 0);
+ ~HWEngine();
+
+ static void exposeToQML();
+ Q_INVOKABLE void getPreview();
+ Q_INVOKABLE void runQuickGame();
+ Q_INVOKABLE void runLocalGame();
+ Q_INVOKABLE QString currentSeed();
+ Q_INVOKABLE void getTeamsList();
+ Q_INVOKABLE void resetGameConfig();
+
+ Q_INVOKABLE void setTheme(const QString & theme);
+ Q_INVOKABLE void setScript(const QString & script);
+ Q_INVOKABLE void setScheme(const QString & scheme);
+ Q_INVOKABLE void setAmmo(const QString & ammo);
+
+ Q_INVOKABLE void tryAddTeam(const QString & teamName);
+ Q_INVOKABLE void tryRemoveTeam(const QString & teamName);
+ Q_INVOKABLE void changeTeamColor(const QString & teamName, int dir);
+
+ Q_INVOKABLE void connect(const QString & host, quint16 port);
+
+ Q_INVOKABLE void sendChatMessage(const QString & msg);
+
+ Q_INVOKABLE void joinRoom(const QString & roomName);
+ Q_INVOKABLE void partRoom(const QString & message);
+
+ Q_INVOKABLE QString myNickname();
+
+signals:
+ void errorMessage(const QString & message);
+ void warningMessage(const QString & message);
+
+ void previewIsRendering();
+ void previewImageChanged();
+ void localTeamAdded(const QString & teamName, int aiLevel);
+ void localTeamRemoved(const QString & teamName);
+
+ void playingTeamAdded(const QString & teamName, int aiLevel, bool isLocal);
+ void playingTeamRemoved(const QString & teamName);
+
+ void teamColorChanged(const QString & teamName, const QString & colorValue);
+ void hedgehogsNumberChanged(const QString & teamName, int hedgehogsNumber);
+
+ void netConnected();
+ void netDisconnected(const QString & message);
+
+ void lobbyClientAdded(const QString & clientName);
+ void lobbyClientRemoved(const QString & clientName, const QString & reason);
+ void lobbyChatLine(const QString & nickname, const QString & line);
+
+ void roomClientAdded(const QString & clientName);
+ void roomClientRemoved(const QString & clientName, const QString & reason);
+ void roomChatLine(const QString & nickname, const QString & line);
+
+ void movedToLobby();
+ void movedToRoom();
+
+ void seedChanged(const QString & seed);
+ void themeChanged(const QString & theme);
+ void scriptChanged(const QString & script);
+ void featureSizeChanged(int featureSize);
+ void mapGenChanged(int mapgen);
+ void mapChanged(const QString & map);
+ void mazeSizeChanged(int mazeSize);
+ void templateChanged(int templ);
+ void ammoChanged(const QString & ammo);
+ void schemeChanged(const QString & scheme);
+
+ void roomAdded(quint32 flags
+ , const QString & name
+ , int players
+ , int teams
+ , const QString & host
+ , const QString & map
+ , const QString & script
+ , const QString & scheme
+ , const QString & weapons);
+ void roomUpdated(const QString & name
+ , quint32 flags
+ , const QString & newName
+ , int players
+ , int teams
+ , const QString & host
+ , const QString & map
+ , const QString & script
+ , const QString & scheme
+ , const QString & weapons);
+ void roomRemoved(const QString & name);
+
+public slots:
+
+private:
+ QQmlEngine * m_engine;
+ QString m_myNickname;
+
+ static void guiMessagesCallback(void * context, MessageType mt, const char * msg, uint32_t len);
+ void fillModels();
+
+private slots:
+ void engineMessageHandler(MessageType mt, const QByteArray &msg);
+};
+
+#endif // HWENGINE_H
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/qmlFrontend/main.cpp Sun Dec 06 20:36:21 2015 +0300
@@ -0,0 +1,27 @@
+#include <QtGui/QGuiApplication>
+#include <QQmlEngine>
+
+#include "qtquick2applicationviewer/qtquick2applicationviewer.h"
+#include "hwengine.h"
+#include "previewimageprovider.h"
+#include "themeiconprovider.h"
+
+
+int main(int argc, char *argv[])
+{
+ QGuiApplication app(argc, argv);
+
+ HWEngine::exposeToQML();
+
+ Q_INIT_RESOURCE(qmlFrontend);
+
+ QtQuick2ApplicationViewer viewer;
+
+ viewer.engine()->addImageProvider(QLatin1String("preview"), new PreviewImageProvider());
+ viewer.engine()->addImageProvider(QLatin1String("theme"), new ThemeIconProvider());
+
+ viewer.setSource(QUrl("qrc:/qml/qmlFrontend/main.qml"));
+ viewer.showExpanded();
+
+ return app.exec();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/qmlFrontend/previewimageprovider.cpp Sun Dec 06 20:36:21 2015 +0300
@@ -0,0 +1,36 @@
+#include "previewimageprovider.h"
+
+PreviewImageProvider::PreviewImageProvider()
+ : QQuickImageProvider(QQuickImageProvider::Pixmap)
+{
+}
+
+QPixmap PreviewImageProvider::requestPixmap(const QString &id, QSize *size, const QSize &requestedSize)
+{
+ Q_UNUSED(id);
+ Q_UNUSED(requestedSize);
+
+ if (size)
+ *size = m_px.size();
+
+ return m_px;
+}
+
+void PreviewImageProvider::setPixmap(const QByteArray &px)
+{
+ QVector<QRgb> colorTable;
+ colorTable.resize(256);
+ for(int i = 0; i < 256; ++i)
+ colorTable[i] = qRgba(255, 255, 0, i);
+
+ const quint8 *buf = (const quint8*) px.constData();
+ QImage im(buf, 256, 128, QImage::Format_Indexed8);
+ im.setColorTable(colorTable);
+
+ m_px = QPixmap::fromImage(im, Qt::ColorOnly);
+ //QPixmap pxres(px.size());
+ //QPainter p(&pxres);
+
+ //p.fillRect(pxres.rect(), linearGrad);
+ //p.drawPixmap(0, 0, px);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/qmlFrontend/previewimageprovider.h Sun Dec 06 20:36:21 2015 +0300
@@ -0,0 +1,21 @@
+#ifndef PREVIEWIMAGEPROVIDER_H
+#define PREVIEWIMAGEPROVIDER_H
+
+#include <QQuickImageProvider>
+#include <QPixmap>
+#include <QSize>
+
+class PreviewImageProvider : public QQuickImageProvider
+{
+public:
+ PreviewImageProvider();
+
+ QPixmap requestPixmap(const QString &id, QSize *size, const QSize &requestedSize);
+
+ void setPixmap(const QByteArray & px);
+
+private:
+ QPixmap m_px;
+};
+
+#endif // PREVIEWIMAGEPROVIDER_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/qmlFrontend/qml/qmlFrontend/Chat.qml Sun Dec 06 20:36:21 2015 +0300
@@ -0,0 +1,137 @@
+import QtQuick 2.0
+import Hedgewars.Engine 1.0
+
+Rectangle {
+ color: "#15193a"
+ radius: 8
+ border.width: 4
+ border.color: "#ea761d"
+
+ ListView {
+ id: chatLines
+ x: 0
+ width: parent.width - clientsList.width
+ anchors.top: parent.top
+ anchors.bottom: input.top
+ focus: true
+ clip: true
+ highlightFollowsCurrentItem: true
+
+ model: ListModel {
+ id: chatLinesModel
+ }
+
+ delegate: Rectangle {
+ id: chatLinesDelegate
+ height: 24
+ width: parent.width
+ color: "transparent"
+
+ Row {
+ spacing: 8;
+ Text {
+ color: "#ffffa0"
+ text: nick
+
+ MouseArea {
+ z: 1
+ anchors.fill: parent
+ onClicked: ;
+ }
+ }
+ Text {
+ color: "#ffffff"
+ text: line
+
+ MouseArea {
+ z: 1
+ anchors.fill: parent
+ onClicked: ;
+ }
+ }
+ }
+
+ }
+
+ function addLine(nickname, line) {
+ chatLinesModel.append({"nick" : nickname, "line": line})
+ if(chatLinesModel.count > 200)
+ chatLinesModel.remove(0)
+ chatLines.currentIndex = chatLinesModel.count - 1
+ }
+ }
+
+ TextInput {
+ id: input
+ x: 0
+ width: chatLines.width
+ height: 24
+ anchors.bottom: parent.bottom
+ color: "#eccd2f"
+
+ onAccepted: {
+ HWEngine.sendChatMessage(text)
+ chatLines.addLine(HWEngine.myNickname(), text)
+ text = ""
+ }
+ }
+
+ ListView {
+ id: clientsList
+ x: parent.width - width
+ width: 100
+ height: parent.height
+ focus: true
+ clip: true
+
+ model: ListModel {
+ id: chatClientsModel
+ }
+
+ delegate: Rectangle {
+ id: chatClientDelegate
+ height: 24
+ width: parent.width
+ color: "transparent"
+
+ Row {
+ Text {
+ color: "#ffffff"
+ text: name
+
+ MouseArea {
+ z: 1
+ anchors.fill: parent
+ onClicked: ;
+ }
+ }
+ }
+ }
+ }
+
+ function addChatLine(nickname, line) {
+ chatLines.addLine(nickname, line)
+ }
+
+ function addClient(clientName) {
+ chatClientsModel.append({"isAdmin": false, "name": clientName})
+ chatLines.addLine("***", qsTr("%1 joined").arg(clientName))
+ }
+
+ function removeClient(clientName, reason) {
+ var i = chatClientsModel.count - 1;
+ while ((i >= 0) && (chatClientsModel.get(i).name !== clientName)) --i;
+
+ if(i >= 0) {
+ chatClientsModel.remove(i, 1);
+ chatLines.addLine("***", qsTr("%1 quit (%2)").arg(clientName).arg(reason))
+ }
+ }
+
+ function clear() {
+ chatClientsModel.clear()
+ chatLinesModel.clear()
+ }
+}
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/qmlFrontend/qml/qmlFrontend/Connect.qml Sun Dec 06 20:36:21 2015 +0300
@@ -0,0 +1,14 @@
+import QtQuick 2.0
+import Hedgewars.Engine 1.0
+
+Rectangle {
+ HWButton {
+ id: btnNetConnect
+ x: 80
+ y: 80
+ width: 256
+ height: 128
+
+ onClicked: HWEngine.connect("netserver.hedgewars.org", 46631);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/qmlFrontend/qml/qmlFrontend/First.qml Sun Dec 06 20:36:21 2015 +0300
@@ -0,0 +1,31 @@
+import QtQuick 2.0
+
+Rectangle {
+ HWButton {
+ id: btnLocalGame
+ x: 8
+ y: 80
+ width: 166
+ height: 166
+
+ onClicked: pages.currentPage = "LocalGame"
+ }
+
+ HWButton {
+ id: btnNetwork
+ x: 192
+ y: 80
+ width: 166
+ height: 166
+
+ onClicked: pages.currentPage = "Connect"
+ }
+
+ HWButton {
+ id: btnAbout
+ x: 100
+ y: 16
+ width: 200
+ height: 50
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/qmlFrontend/qml/qmlFrontend/GameConfig.qml Sun Dec 06 20:36:21 2015 +0300
@@ -0,0 +1,313 @@
+import QtQuick 2.0
+import Hedgewars.Engine 1.0
+
+
+Rectangle {
+ Column {
+ spacing: 8
+
+ HWButton {
+ id: btnPreview
+ width: 256
+ height: 128
+
+ onClicked: HWEngine.getPreview()
+
+ Connections {
+ target: HWEngine
+ onPreviewImageChanged: previewImage.source = "image://preview/image"
+ onPreviewIsRendering: previewImage.source = "qrc:/res/iconTime.png"
+ }
+
+ Image {
+ id: previewImage
+ x: 0
+ y: 0
+ width: 256
+ height: 128
+ cache: false
+ source: "qrc:/res/iconTime.png"
+ }
+ }
+
+ HWComboBox {
+ id: cbTheme
+ width: 256
+ height: 64
+
+ model: themesModel
+ delegate: Rectangle {
+ height: 25
+ width: 100
+ color: "transparent"
+
+ property alias itemIconSource: themeIcon.source
+ property alias itemText: themeName.text
+
+ Row {
+ Image {id: themeIcon; width: height; height: parent.height; source: "image://theme/" + modelData}
+ Text {id: themeName; text: modelData }
+ }
+
+ MouseArea {
+ z: 1
+ anchors.fill: parent
+ onClicked: {
+ cbTheme.currentIndex = index
+ HWEngine.setTheme(themeName.text)
+ }
+ }
+ }
+
+ Connections {
+ target: HWEngine
+ onThemeChanged: cbTheme.showItem({"iconSource" : "image://theme/" + theme, "text" : theme});
+ }
+ }
+
+ HWComboBox {
+ id: cbScript
+ width: 256
+ height: 32
+
+ model: scriptsModel
+ delegate: Rectangle {
+ height: 25
+ width: 100
+ color: "transparent"
+
+ property string itemIconSource: ""
+ property alias itemText: scriptName.text
+
+ Row {
+ //Image {id: themeIcon; width: height; height: parent.height; source: "image://theme/" + modelData}
+ Text {id: scriptName; text: modelData }
+ }
+
+ MouseArea {
+ z: 1
+ anchors.fill: parent
+ onClicked: {
+ cbScript.currentIndex = index
+ HWEngine.setScript(scriptName.text)
+ }
+ }
+ }
+ Connections {
+ target: HWEngine
+ onScriptChanged: cbScript.showItem({"iconSource" : "", "text" : script});
+ }
+ }
+
+ HWComboBox {
+ id: cbScheme
+ width: 256
+ height: 32
+
+ model: schemesModel
+ delegate: Rectangle {
+ height: 25
+ width: 100
+ color: "transparent"
+
+ property string itemIconSource: ""
+ property alias itemText: schemeName.text
+
+ Row {
+ //Image {id: themeIcon; width: height; height: parent.height; source: "image://theme/" + modelData}
+ Text {id: schemeName; text: modelData }
+ }
+
+ MouseArea {
+ z: 1
+ anchors.fill: parent
+ onClicked: {
+ cbScheme.currentIndex = index
+ HWEngine.setScheme(schemeName.text)
+ }
+ }
+ }
+ Connections {
+ target: HWEngine
+ onSchemeChanged: cbScheme.showItem({"iconSource" : "", "text" : scheme});
+ }
+ }
+
+
+ HWComboBox {
+ id: cbAmmo
+ width: 256
+ height: 32
+
+ model: ammosModel
+ delegate: Rectangle {
+ height: 25
+ width: 100
+ color: "transparent"
+
+ property string itemIconSource: ""
+ property alias itemText: ammoName.text
+
+ Row {
+ //Image {id: themeIcon; width: height; height: parent.height; source: "image://theme/" + modelData}
+ Text {id: ammoName; text: modelData }
+ }
+
+ MouseArea {
+ z: 1
+ anchors.fill: parent
+ onClicked: {
+ cbAmmo.currentIndex = index
+ HWEngine.setAmmo(ammoName.text)
+ }
+ }
+ }
+ Connections {
+ target: HWEngine
+ onAmmoChanged: cbAmmo.showItem({"iconSource" : "", "text" : ammo});
+ }
+ }
+ }
+
+ ListView {
+ id: playingTeamsList
+ x: 440
+ y: 16
+ width: 100
+ height: 192
+ highlight: Rectangle { color: "#eaea00"; radius: 4 }
+ focus: true
+ clip: true
+
+ model: ListModel {
+ id: playingTeamsModel
+ }
+
+ delegate: Rectangle {
+ id: teamDelegate
+ height: 24
+ width: parent.width
+ radius: 8
+ border.width: 2
+ border.color: "#eaea00"
+
+ Row {
+ Rectangle {
+ height: 20
+ width: height
+ color: teamColor
+ border.width: 2
+ border.color: "#eaea00"
+
+ MouseArea {
+ z: 1
+ anchors.fill: parent
+ acceptedButtons: Qt.LeftButton | Qt.RightButton
+ onClicked: {
+ if (mouse.button === Qt.LeftButton)
+ HWEngine.changeTeamColor(name, 1)
+ else if (mouse.button === Qt.RightButton)
+ HWEngine.changeTeamColor(name, -1)
+ }
+ onWheel: HWEngine.changeTeamColor(name, -wheel.angleDelta.y)
+ }
+ }
+
+ Text {
+ text: name
+ MouseArea {
+ z: 1
+ anchors.fill: parent
+ onClicked: HWEngine.tryRemoveTeam(name)
+ }
+ }
+
+ Text {
+ text: hedgehogsNumber
+ }
+ }
+
+
+ }
+
+ Connections {
+ target: HWEngine
+ onPlayingTeamAdded: playingTeamsModel.append({
+ "aiLevel": aiLevel
+ , "name": teamName
+ , "local": isLocal
+ , "hedgehogsNumber" : 4
+ , "teamColor": "#000000"
+ })
+ onPlayingTeamRemoved: {
+ var i = playingTeamsModel.count - 1;
+ while ((i >= 0) && (playingTeamsModel.get(i).name !== teamName)) --i
+
+ if(i >= 0) playingTeamsModel.remove(i, 1)
+ }
+ onTeamColorChanged: {
+ var i = playingTeamsModel.count - 1;
+ while ((i >= 0) && (playingTeamsModel.get(i).name !== teamName)) --i
+
+ if(i >= 0) playingTeamsModel.setProperty(i, "teamColor", colorValue)
+ }
+ onHedgehogsNumberChanged: {
+ var i = playingTeamsModel.count - 1;
+ while ((i >= 0) && (playingTeamsModel.get(i).name !== teamName)) --i
+
+ if(i >= 0) playingTeamsModel.setProperty(i, "hedgehogsNumber", hedgehogsNumber)
+ }
+ }
+ }
+
+ ListView {
+ id: localTeamsList
+ x: 440
+ y: 224
+ width: 100
+ height: 192
+ highlight: Rectangle { color: "#eaea00"; radius: 4 }
+ focus: true
+ clip: true
+
+ model: ListModel {
+ id: localTeamsModel
+ }
+
+ delegate: Rectangle {
+ id: localTeamDelegate
+ height: 24
+ width: parent.width
+ radius: 8
+ border.width: 2
+ border.color: "#eaea00"
+
+ Row {
+ Text { text: name }
+ }
+
+ MouseArea {
+ z: 1
+ anchors.fill: parent
+ onClicked: HWEngine.tryAddTeam(name)
+ }
+ }
+
+ Connections {
+ target: HWEngine
+ onLocalTeamAdded: localTeamsModel.append({"aiLevel": aiLevel, "name": teamName})
+ onLocalTeamRemoved: {
+ var i = localTeamsModel.count - 1;
+ while ((i >= 0) && (localTeamsModel.get(i).name !== teamName)) --i
+
+ if(i >= 0) localTeamsModel.remove(i, 1)
+ }
+ }
+ }
+
+ Component.onCompleted: {
+ HWEngine.resetGameConfig()
+ HWEngine.getTeamsList()
+ HWEngine.getPreview()
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/qmlFrontend/qml/qmlFrontend/HWButton.qml Sun Dec 06 20:36:21 2015 +0300
@@ -0,0 +1,42 @@
+import QtQuick 2.0
+
+Rectangle {
+ id: hwbutton
+ width: 360
+ height: 360
+ color: "#15193a"
+ radius: 8
+ border.width: 4
+ opacity: 1
+
+ signal clicked()
+
+ Behavior on border.color {
+ ColorAnimation {}
+ }
+
+ MouseArea {
+ id: mousearea
+ anchors.fill: parent
+ hoverEnabled: true
+ onClicked: parent.clicked()
+ }
+
+ states: [
+ State {
+ when: mousearea.containsMouse
+
+ PropertyChanges {
+ target: hwbutton
+ border.color: "#eaea00"
+ }
+ }
+ , State {
+ when: !mousearea.containsMouse
+
+ PropertyChanges {
+ target: hwbutton
+ border.color: "#ea761d"
+ }
+ }]
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/qmlFrontend/qml/qmlFrontend/HWComboBox.qml Sun Dec 06 20:36:21 2015 +0300
@@ -0,0 +1,63 @@
+import QtQuick 2.0
+import QtQuick.Window 2.1
+
+HWButton {
+ property alias model: itemsList.model
+ property alias delegate: itemsList.delegate
+ property alias currentIndex: itemsList.currentIndex
+
+ Window {
+ id: selection
+ visibility: Window.Hidden
+ modality: Qt.WindowModal
+ flags: Qt.Dialog
+
+ ListView {
+ id: itemsList
+ x: 0
+ y: 64
+ anchors.fill: parent
+ anchors.bottomMargin: 32
+ highlight: Rectangle { color: "#eaea00"; radius: 4 }
+ focus: true
+
+ onCurrentItemChanged: {
+ cbIcon.source = currentItem.itemIconSource
+ cbText.text = currentItem.itemText
+ }
+ }
+
+ HWButton {
+ x: parent.width - 32
+ y: parent.height - 32
+ width: 32
+ height: 32
+
+ onClicked: selection.visibility = Window.Hidden;
+ }
+ }
+
+ Row {
+ anchors.fill: parent
+ anchors.margins: 4
+
+ Image {
+ id: cbIcon
+ width: height
+ height: parent.height
+ }
+
+ Text {
+ id: cbText
+ height: parent.height
+ color: "#f3e520"
+ }
+ }
+
+ function showItem(item) {
+ cbIcon.source = item.iconSource
+ cbText.text = item.text
+ }
+
+ onClicked: selection.visibility = Window.Windowed
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/qmlFrontend/qml/qmlFrontend/Lobby.qml Sun Dec 06 20:36:21 2015 +0300
@@ -0,0 +1,111 @@
+import QtQuick 2.0
+import Hedgewars.Engine 1.0
+
+Rectangle {
+ ListView {
+ id: roomsList
+ x: 20
+ y: 0
+ width: parent.width
+ height: parent.height - x
+ focus: true
+ clip: true
+
+ model: ListModel {
+ id: roomsListModel
+ }
+
+ delegate: Rectangle {
+ id: roomDelegate
+ height: 24
+ width: parent.width
+ color: "transparent"
+
+ Row {
+ spacing: 8;
+ Text {
+ text: name
+ }
+ Text {
+ text: players + " / " + teams
+ }
+ Text {
+ text: host
+ }
+ Text {
+ text: map
+ }
+ Text {
+ text: script
+ }
+ Text {
+ text: scheme
+ }
+ Text {
+ text: weapons
+ }
+ }
+
+ MouseArea {
+ z: 1
+ anchors.fill: parent
+ onDoubleClicked: HWEngine.joinRoom(name);
+ }
+ }
+
+ Connections {
+ target: HWEngine
+ onRoomAdded: roomsListModel.append({
+ "name" : name
+ , "players": players
+ , "teams": teams
+ , "host": host
+ , "map": map
+ , "script": script
+ , "scheme": scheme
+ , "weapons": weapons
+ })
+ onRoomUpdated: {
+ var i = roomsListModel.count - 1;
+ while ((i >= 0) && (roomsListModel.get(i).name !== name)) --i
+
+ if(i >= 0) {
+ roomsListModel.set(i, {
+ "name" : newName
+ , "players": players
+ , "teams": teams
+ , "host": host
+ , "map": map
+ , "script": script
+ , "scheme": scheme
+ , "weapons": weapons
+ })
+ }
+ }
+ onRoomRemoved: {
+ var i = roomsListModel.count - 1;
+ while ((i >= 0) && (roomsListModel.get(i).name !== name)) --i
+
+ if(i >= 0) roomsListModel.remove(i, 1)
+ }
+ }
+ }
+
+ Chat {
+ id: lobbyChat;
+ x: 0;
+ y: 300;
+ width: parent.width;
+ height: parent.height - y;
+
+ Connections {
+ target: HWEngine
+ onNetConnected: lobbyChat.clear()
+ onLobbyChatLine: lobbyChat.addChatLine(nickname, line)
+ onLobbyClientAdded: lobbyChat.addClient(clientName)
+ onLobbyClientRemoved: lobbyChat.removeClient(clientName, reason)
+ }
+ }
+}
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/qmlFrontend/qml/qmlFrontend/LocalGame.qml Sun Dec 06 20:36:21 2015 +0300
@@ -0,0 +1,34 @@
+import QtQuick 2.0
+import Hedgewars.Engine 1.0
+
+Rectangle {
+ HWButton {
+ id: btnQuickGame
+ x: 8
+ y: 66
+ width: 150
+ height: 150
+
+ onClicked: HWEngine.runQuickGame()
+ }
+
+ HWButton {
+ id: btnMultiplayer
+ x: 192
+ y: 66
+ width: 150
+ height: 150
+
+ onClicked: pages.currentPage = "Multiplayer"
+ }
+
+ HWButton {
+ id: btnBack
+ width: 40
+ height: 40
+ anchors.bottom: parent.bottom
+ anchors.left: parent.left
+
+ onClicked: pages.currentPage = "First"
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/qmlFrontend/qml/qmlFrontend/Multiplayer.qml Sun Dec 06 20:36:21 2015 +0300
@@ -0,0 +1,32 @@
+import QtQuick 2.0
+import Hedgewars.Engine 1.0
+
+Item {
+ GameConfig {
+ anchors.left: parent.left
+ anchors.top: parent.top
+ anchors.right: parent.right
+ anchors.bottom: btnRunGame.top
+ }
+
+ HWButton {
+ id: btnBack
+ width: 40
+ height: 40
+ anchors.bottom: parent.bottom
+ anchors.left: parent.left
+
+ onClicked: pages.currentPage = "LocalGame"
+ }
+
+ HWButton {
+ id: btnRunGame
+ width: 40
+ height: 40
+ anchors.bottom: parent.bottom
+ anchors.right: parent.right
+
+ onClicked: HWEngine.runLocalGame()
+ }
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/qmlFrontend/qml/qmlFrontend/Room.qml Sun Dec 06 20:36:21 2015 +0300
@@ -0,0 +1,38 @@
+import QtQuick 2.0
+import Hedgewars.Engine 1.0
+
+Rectangle {
+ HWButton {
+ id: btnBack
+ width: 40
+ height: 40
+ anchors.left: parent.left
+ anchors.bottom: parent.bottom
+
+ onClicked: HWEngine.partRoom("")
+ }
+
+ GameConfig {
+ id: gameConfig
+ anchors.left: parent.left
+ anchors.top: parent.top
+ anchors.right: parent.right
+ anchors.bottom: roomChat.top
+ }
+
+ Chat {
+ id: roomChat;
+ x: 0;
+ width: parent.width;
+ height: 250;
+ anchors.bottom: btnBack.top
+
+ Connections {
+ target: HWEngine
+ onMovedToRoom: roomChat.clear()
+ onRoomChatLine: roomChat.addChatLine(nickname, line)
+ onRoomClientAdded: roomChat.addClient(clientName)
+ onRoomClientRemoved: roomChat.removeClient(clientName, reason)
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/qmlFrontend/qml/qmlFrontend/main.qml Sun Dec 06 20:36:21 2015 +0300
@@ -0,0 +1,94 @@
+import QtQuick 2.0
+import Hedgewars.Engine 1.0
+
+Rectangle {
+ id: pages
+ width: 800
+ height: 600
+
+ property variant pagesList : [
+ "First"
+ , "LocalGame"
+ , "Multiplayer"
+ , "Connect"
+ , "Lobby"
+ , "Room"
+ ];
+
+ property string currentPage : "First";
+
+ Repeater {
+ id: pagesView
+ model: pagesList
+
+ function loadPage(page) {
+ // somehow load the page (when Loader has asynchronous == true)
+ }
+
+ delegate: Loader {
+ active: false
+ asynchronous: false
+ anchors.fill: parent
+ visible: (currentPage === modelData)
+ source: "%1.qml".arg(modelData)
+ onVisibleChanged: loadIfNotLoaded();
+ Component.onCompleted: loadIfNotLoaded();
+
+ function loadIfNotLoaded ()
+ {
+ if (visible && !active)
+ active = true;
+ }
+ }
+ }
+
+ Rectangle {
+ id: warningsBox
+ y: parent.height - height
+ width: parent.width - 120
+ height: 80
+ anchors.horizontalCenter: parent.horizontalCenter
+ color: "#7e3232"
+ border.color: "#d3ec2d"
+ visible: false
+ z: 2
+
+ function showMessage(message) {
+ msgBox.text = message
+ visible = true
+ }
+
+ Text {
+ id: msgBox
+ x: 0
+ y: 0
+ height: parent.height
+ font.pixelSize: 12
+ wrapMode: Text.Wrap
+ }
+ HWButton {
+ id: closeButton
+ x: parent.width - width
+ y: 0
+ width: 40
+ height: 40
+ onClicked: warningsBox.visible = false
+ }
+ }
+
+ Connections {
+ target: HWEngine
+ onNetConnected: {
+ pagesView.loadPage("Lobby");
+ pagesView.loadPage("Room");
+ }
+ onMovedToLobby: currentPage = "Lobby";
+ onMovedToRoom: currentPage = "Room";
+ onNetDisconnected: {
+ currentPage = "First";
+ warningsBox.showMessage(message);
+ }
+ onWarningMessage: warningsBox.showMessage(message);
+ onErrorMessage: warningsBox.showMessage(message);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/qmlFrontend/qmlFrontend.qrc Sun Dec 06 20:36:21 2015 +0300
@@ -0,0 +1,16 @@
+<RCC>
+ <qresource prefix="/">
+ <file>qml/qmlFrontend/First.qml</file>
+ <file>qml/qmlFrontend/GameConfig.qml</file>
+ <file>qml/qmlFrontend/HWButton.qml</file>
+ <file>qml/qmlFrontend/HWComboBox.qml</file>
+ <file>qml/qmlFrontend/LocalGame.qml</file>
+ <file>qml/qmlFrontend/main.qml</file>
+ <file>qml/qmlFrontend/Connect.qml</file>
+ <file>qml/qmlFrontend/Chat.qml</file>
+ <file>qml/qmlFrontend/Room.qml</file>
+ <file>qml/qmlFrontend/Lobby.qml</file>
+ <file>qml/qmlFrontend/Multiplayer.qml</file>
+ <file>res/iconTime.png</file>
+ </qresource>
+</RCC>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/qmlFrontend/qtquick2applicationviewer/qtquick2applicationviewer.cpp Sun Dec 06 20:36:21 2015 +0300
@@ -0,0 +1,81 @@
+// checksum 0x4f6f version 0x90005
+/*
+ This file was generated by the Qt Quick 2 Application wizard of Qt Creator.
+ QtQuick2ApplicationViewer is a convenience class containing mobile device specific
+ code such as screen orientation handling. Also QML paths and debugging are
+ handled here.
+ It is recommended not to modify this file, since newer versions of Qt Creator
+ may offer an updated version of it.
+*/
+
+#include "qtquick2applicationviewer.h"
+
+#include <QtCore/QCoreApplication>
+#include <QtCore/QDir>
+#include <QtQml/QQmlEngine>
+
+class QtQuick2ApplicationViewerPrivate
+{
+ QString mainQmlFile;
+ friend class QtQuick2ApplicationViewer;
+ static QString adjustPath(const QString &path);
+};
+
+QString QtQuick2ApplicationViewerPrivate::adjustPath(const QString &path)
+{
+#if defined(Q_OS_MAC)
+ if (!QDir::isAbsolutePath(path))
+ return QString::fromLatin1("%1/../Resources/%2")
+ .arg(QCoreApplication::applicationDirPath(), path);
+#elif defined(Q_OS_BLACKBERRY)
+ if (!QDir::isAbsolutePath(path))
+ return QString::fromLatin1("app/native/%1").arg(path);
+#elif !defined(Q_OS_ANDROID)
+ QString pathInInstallDir =
+ QString::fromLatin1("%1/../%2").arg(QCoreApplication::applicationDirPath(), path);
+ if (QFileInfo(pathInInstallDir).exists())
+ return pathInInstallDir;
+ pathInInstallDir =
+ QString::fromLatin1("%1/%2").arg(QCoreApplication::applicationDirPath(), path);
+ if (QFileInfo(pathInInstallDir).exists())
+ return pathInInstallDir;
+#endif
+ return path;
+}
+
+QtQuick2ApplicationViewer::QtQuick2ApplicationViewer(QWindow *parent)
+ : QQuickView(parent)
+ , d(new QtQuick2ApplicationViewerPrivate())
+{
+ connect(engine(), SIGNAL(quit()), SLOT(close()));
+ setResizeMode(QQuickView::SizeRootObjectToView);
+}
+
+QtQuick2ApplicationViewer::~QtQuick2ApplicationViewer()
+{
+ delete d;
+}
+
+void QtQuick2ApplicationViewer::setMainQmlFile(const QString &file)
+{
+ d->mainQmlFile = QtQuick2ApplicationViewerPrivate::adjustPath(file);
+#ifdef Q_OS_ANDROID
+ setSource(QUrl(QLatin1String("assets:/")+d->mainQmlFile));
+#else
+ setSource(QUrl::fromLocalFile(d->mainQmlFile));
+#endif
+}
+
+void QtQuick2ApplicationViewer::addImportPath(const QString &path)
+{
+ engine()->addImportPath(QtQuick2ApplicationViewerPrivate::adjustPath(path));
+}
+
+void QtQuick2ApplicationViewer::showExpanded()
+{
+#if defined(Q_WS_SIMULATOR) || defined(Q_OS_QNX)
+ showFullScreen();
+#else
+ show();
+#endif
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/qmlFrontend/qtquick2applicationviewer/qtquick2applicationviewer.h Sun Dec 06 20:36:21 2015 +0300
@@ -0,0 +1,33 @@
+// checksum 0xfde6 version 0x90005
+/*
+ This file was generated by the Qt Quick 2 Application wizard of Qt Creator.
+ QtQuick2ApplicationViewer is a convenience class containing mobile device specific
+ code such as screen orientation handling. Also QML paths and debugging are
+ handled here.
+ It is recommended not to modify this file, since newer versions of Qt Creator
+ may offer an updated version of it.
+*/
+
+#ifndef QTQUICK2APPLICATIONVIEWER_H
+#define QTQUICK2APPLICATIONVIEWER_H
+
+#include <QtQuick/QQuickView>
+
+class QtQuick2ApplicationViewer : public QQuickView
+{
+ Q_OBJECT
+
+public:
+ explicit QtQuick2ApplicationViewer(QWindow *parent = 0);
+ virtual ~QtQuick2ApplicationViewer();
+
+ void setMainQmlFile(const QString &file);
+ void addImportPath(const QString &path);
+
+ void showExpanded();
+
+private:
+ class QtQuick2ApplicationViewerPrivate *d;
+};
+
+#endif // QTQUICK2APPLICATIONVIEWER_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/qmlFrontend/qtquick2applicationviewer/qtquick2applicationviewer.pri Sun Dec 06 20:36:21 2015 +0300
@@ -0,0 +1,180 @@
+# checksum 0x7b0d version 0x90005
+# This file was generated by the Qt Quick 2 Application wizard of Qt Creator.
+# The code below adds the QtQuick2ApplicationViewer to the project and handles
+# the activation of QML debugging.
+# It is recommended not to modify this file, since newer versions of Qt Creator
+# may offer an updated version of it.
+
+QT += qml quick
+
+SOURCES += $$PWD/qtquick2applicationviewer.cpp
+HEADERS += $$PWD/qtquick2applicationviewer.h
+INCLUDEPATH += $$PWD
+# This file was generated by an application wizard of Qt Creator.
+# The code below handles deployment to Android and Maemo, aswell as copying
+# of the application data to shadow build directories on desktop.
+# It is recommended not to modify this file, since newer versions of Qt Creator
+# may offer an updated version of it.
+
+defineTest(qtcAddDeployment) {
+for(deploymentfolder, DEPLOYMENTFOLDERS) {
+ item = item$${deploymentfolder}
+ greaterThan(QT_MAJOR_VERSION, 4) {
+ itemsources = $${item}.files
+ } else {
+ itemsources = $${item}.sources
+ }
+ $$itemsources = $$eval($${deploymentfolder}.source)
+ itempath = $${item}.path
+ $$itempath= $$eval($${deploymentfolder}.target)
+ export($$itemsources)
+ export($$itempath)
+ DEPLOYMENT += $$item
+}
+
+MAINPROFILEPWD = $$PWD
+
+android-no-sdk {
+ for(deploymentfolder, DEPLOYMENTFOLDERS) {
+ item = item$${deploymentfolder}
+ itemfiles = $${item}.files
+ $$itemfiles = $$eval($${deploymentfolder}.source)
+ itempath = $${item}.path
+ $$itempath = /data/user/qt/$$eval($${deploymentfolder}.target)
+ export($$itemfiles)
+ export($$itempath)
+ INSTALLS += $$item
+ }
+
+ target.path = /data/user/qt
+
+ export(target.path)
+ INSTALLS += target
+} else:android {
+ for(deploymentfolder, DEPLOYMENTFOLDERS) {
+ item = item$${deploymentfolder}
+ itemfiles = $${item}.files
+ $$itemfiles = $$eval($${deploymentfolder}.source)
+ itempath = $${item}.path
+ $$itempath = /assets/$$eval($${deploymentfolder}.target)
+ export($$itemfiles)
+ export($$itempath)
+ INSTALLS += $$item
+ }
+
+ x86 {
+ target.path = /libs/x86
+ } else: armeabi-v7a {
+ target.path = /libs/armeabi-v7a
+ } else {
+ target.path = /libs/armeabi
+ }
+
+ export(target.path)
+ INSTALLS += target
+} else:win32 {
+ copyCommand =
+ for(deploymentfolder, DEPLOYMENTFOLDERS) {
+ source = $$MAINPROFILEPWD/$$eval($${deploymentfolder}.source)
+ source = $$replace(source, /, \\)
+ sourcePathSegments = $$split(source, \\)
+ target = $$OUT_PWD/$$eval($${deploymentfolder}.target)/$$last(sourcePathSegments)
+ target = $$replace(target, /, \\)
+ target ~= s,\\\\\\.?\\\\,\\,
+ !isEqual(source,$$target) {
+ !isEmpty(copyCommand):copyCommand += &&
+ isEqual(QMAKE_DIR_SEP, \\) {
+ copyCommand += $(COPY_DIR) \"$$source\" \"$$target\"
+ } else {
+ source = $$replace(source, \\\\, /)
+ target = $$OUT_PWD/$$eval($${deploymentfolder}.target)
+ target = $$replace(target, \\\\, /)
+ copyCommand += test -d \"$$target\" || mkdir -p \"$$target\" && cp -r \"$$source\" \"$$target\"
+ }
+ }
+ }
+ !isEmpty(copyCommand) {
+ copyCommand = @echo Copying application data... && $$copyCommand
+ copydeploymentfolders.commands = $$copyCommand
+ first.depends = $(first) copydeploymentfolders
+ export(first.depends)
+ export(copydeploymentfolders.commands)
+ QMAKE_EXTRA_TARGETS += first copydeploymentfolders
+ }
+} else:unix {
+ maemo5 {
+ desktopfile.files = $${TARGET}.desktop
+ desktopfile.path = /usr/share/applications/hildon
+ icon.files = $${TARGET}64.png
+ icon.path = /usr/share/icons/hicolor/64x64/apps
+ } else:!isEmpty(MEEGO_VERSION_MAJOR) {
+ desktopfile.files = $${TARGET}_harmattan.desktop
+ desktopfile.path = /usr/share/applications
+ icon.files = $${TARGET}80.png
+ icon.path = /usr/share/icons/hicolor/80x80/apps
+ } else { # Assumed to be a Desktop Unix
+ copyCommand =
+ for(deploymentfolder, DEPLOYMENTFOLDERS) {
+ source = $$MAINPROFILEPWD/$$eval($${deploymentfolder}.source)
+ source = $$replace(source, \\\\, /)
+ macx {
+ target = $$OUT_PWD/$${TARGET}.app/Contents/Resources/$$eval($${deploymentfolder}.target)
+ } else {
+ target = $$OUT_PWD/$$eval($${deploymentfolder}.target)
+ }
+ target = $$replace(target, \\\\, /)
+ sourcePathSegments = $$split(source, /)
+ targetFullPath = $$target/$$last(sourcePathSegments)
+ targetFullPath ~= s,/\\.?/,/,
+ !isEqual(source,$$targetFullPath) {
+ !isEmpty(copyCommand):copyCommand += &&
+ copyCommand += $(MKDIR) \"$$target\"
+ copyCommand += && $(COPY_DIR) \"$$source\" \"$$target\"
+ }
+ }
+ !isEmpty(copyCommand) {
+ copyCommand = @echo Copying application data... && $$copyCommand
+ copydeploymentfolders.commands = $$copyCommand
+ first.depends = $(first) copydeploymentfolders
+ export(first.depends)
+ export(copydeploymentfolders.commands)
+ QMAKE_EXTRA_TARGETS += first copydeploymentfolders
+ }
+ }
+ !isEmpty(target.path) {
+ installPrefix = $${target.path}
+ } else {
+ installPrefix = /opt/$${TARGET}
+ }
+ for(deploymentfolder, DEPLOYMENTFOLDERS) {
+ item = item$${deploymentfolder}
+ itemfiles = $${item}.files
+ $$itemfiles = $$eval($${deploymentfolder}.source)
+ itempath = $${item}.path
+ $$itempath = $${installPrefix}/$$eval($${deploymentfolder}.target)
+ export($$itemfiles)
+ export($$itempath)
+ INSTALLS += $$item
+ }
+
+ !isEmpty(desktopfile.path) {
+ export(icon.files)
+ export(icon.path)
+ export(desktopfile.files)
+ export(desktopfile.path)
+ INSTALLS += icon desktopfile
+ }
+
+ isEmpty(target.path) {
+ target.path = $${installPrefix}/bin
+ export(target.path)
+ }
+ INSTALLS += target
+}
+
+export (ICON)
+export (INSTALLS)
+export (DEPLOYMENT)
+export (LIBS)
+export (QMAKE_EXTRA_TARGETS)
+}
Binary file qmlFrontend/res/iconTime.png has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/qmlFrontend/themeiconprovider.cpp Sun Dec 06 20:36:21 2015 +0300
@@ -0,0 +1,38 @@
+#include <QByteArray>
+#include <QDebug>
+
+#include "themeiconprovider.h"
+#include "flib.h"
+
+ThemeIconProvider::ThemeIconProvider()
+ : QQuickImageProvider(QQuickImageProvider::Image)
+{
+ getThemeIcon = 0;
+}
+
+void ThemeIconProvider::setFileContentsFunction(getThemeIcon_t *f)
+{
+ getThemeIcon = f;
+}
+
+QImage ThemeIconProvider::requestImage(const QString &id, QSize *size, const QSize &requestedSize)
+{
+ Q_UNUSED(requestedSize);
+
+ if(!getThemeIcon)
+ return QImage();
+
+ QByteArray buf;
+ buf.resize(65536);
+
+ char * bufptr = buf.data();
+ uint32_t fileSize = getThemeIcon(id.toUtf8().data(), bufptr, buf.size());
+ buf.truncate(fileSize);
+ //qDebug() << "ThemeIconProvider file size = " << fileSize;
+
+ QImage img = fileSize ? QImage::fromData(buf) : QImage(16, 16, QImage::Format_ARGB32);
+
+ if (size)
+ *size = img.size();
+ return img;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/qmlFrontend/themeiconprovider.h Sun Dec 06 20:36:21 2015 +0300
@@ -0,0 +1,21 @@
+#ifndef THEMEICONPROVIDER_H
+#define THEMEICONPROVIDER_H
+
+#include <QQuickImageProvider>
+#include <QImage>
+
+#include "flib.h"
+
+class ThemeIconProvider : public QQuickImageProvider
+{
+public:
+ ThemeIconProvider();
+
+ void setFileContentsFunction(getThemeIcon_t *f);
+
+ QImage requestImage(const QString &id, QSize *size, const QSize &requestedSize);
+private:
+ getThemeIcon_t *getThemeIcon;
+};
+
+#endif // THEMEICONPROVIDER_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/protocolParser.hs Sun Dec 06 20:36:21 2015 +0300
@@ -0,0 +1,203 @@
+module Main where
+
+import Text.PrettyPrint.HughesPJ
+import qualified Data.MultiMap as MM
+import Data.Maybe
+import Data.List
+import Data.Char
+import qualified Data.Set as Set
+
+data HWProtocol = Command String [CmdParam]
+ deriving Show
+
+instance Ord HWProtocol where
+ (Command a _) `compare` (Command b _) = a `compare` b
+instance Eq HWProtocol where
+ (Command a _) == (Command b _) = a == b
+
+data CmdParam = Skip
+ | SS
+ | LS
+ | IntP
+ | Many [CmdParam]
+ deriving Show
+
+data ParseTree = PTPrefix String [ParseTree]
+ | PTCommand String HWProtocol
+ deriving Show
+
+cmd = Command
+cmd1 s p = Command s [p]
+cmd2 s p1 p2 = Command s [p1, p2]
+
+cmdName (Command n _) = n
+
+cmdParams2str (Command _ p) = "TCmdParam" ++ concatMap f p
+ where
+ f Skip = ""
+ f SS = "S"
+ f LS = "L"
+ f IntP = "i"
+ f (Many p) = ""
+
+cmdParams2handlerType (Command _ p) = "handler_" ++ concatMap f p
+ where
+ f Skip = "_"
+ f SS = "S"
+ f LS = "L"
+ f IntP = "i"
+ f (Many p) = 'M' : concatMap f p
+
+cmdParams2record cmd@(Command _ p) = renderStyle style{lineLength = 80} $
+ text "type " <> text (cmdParams2str cmd)
+ <> text " = record" $+$ nest 4 (
+ vcat (map (uncurry f) $ zip [1..] $ filter isRendered p)
+ $+$ text "end;")
+ where
+ isRendered Skip = False
+ isRendered (Many _) = False
+ isRendered _ = True
+ f n Skip = empty
+ f n SS = text "str" <> int n <> text ": shortstring;"
+ f n LS = text "str" <> int n <> text ": longstring;"
+ f n IntP = text "param" <> int n <> text ": LongInt;"
+ f _ (Many _) = empty
+
+commandsDescription = [
+ cmd "CONNECTED" [Skip, IntP]
+ , cmd1 "NICK" SS
+ , cmd1 "PROTO" IntP
+ , cmd1 "ASKPASSWORD" SS
+ , cmd1 "SERVER_AUTH" SS
+ , cmd1 "JOINING" SS
+ , cmd1 "TEAM_ACCEPTED" SS
+ , cmd2 "HH_NUM" SS SS
+ , cmd2 "TEAM_COLOR" SS SS
+ , cmd1 "BANLIST" $ Many [SS]
+ , cmd1 "JOINED" $ Many [SS]
+ , cmd1 "LOBBY:JOINED" $ Many [SS]
+ , cmd2 "LOBBY:LEFT" SS LS
+ , cmd2 "CLIENT_FLAGS" SS $ Many [SS]
+ , cmd2 "LEFT" SS LS
+ , cmd1 "SERVER_MESSAGE" LS
+ , cmd1 "ERROR" LS
+ , cmd1 "NOTICE" LS
+ , cmd1 "WARNING" LS
+ , cmd1 "EM" $ Many [LS]
+ , cmd1 "PING" $ Many [SS]
+ , cmd2 "CHAT" SS LS
+ , cmd2 "SERVER_VARS" SS LS
+ , cmd2 "BYE" SS LS
+ , cmd1 "INFO" $ Many [SS]
+ , cmd1 "ROOM~ADD" $ Many [SS]
+ , cmd1 "ROOM~UPD" $ Many [SS]
+ , cmd1 "ROOM~DEL" SS
+ , cmd1 "ROOMS" $ Many [SS]
+ , cmd "KICKED" []
+ , cmd "RUN_GAME" []
+ , cmd "ROUND_FINISHED" []
+ , cmd1 "ADD_TEAM" $ Many [SS]
+ , cmd1 "REMOVE_TEAM" SS
+ , cmd1 "CFG~MAP" SS
+ , cmd1 "CFG~SEED" SS
+ , cmd1 "CFG~SCHEME" $ Many [SS]
+ , cmd1 "CFG~THEME" SS
+ , cmd1 "CFG~TEMPLATE" IntP
+ , cmd1 "CFG~MAPGEN" IntP
+ , cmd1 "CFG~FEATURE_SIZE" IntP
+ , cmd1 "CFG~MAZE_SIZE" IntP
+ , cmd1 "CFG~SCRIPT" SS
+ , cmd1 "CFG~DRAWNMAP" LS
+ , cmd2 "CFG~AMMO" SS LS
+ , cmd1 "CFG~FULLMAPCONFIG" $ Many [LS]
+ ]
+
+hasMany = any isMany
+isMany (Many _) = True
+isMany _ = False
+
+unknown = Command "__UNKNOWN__" [Many [SS]]
+unknowncmd = PTPrefix "$" [PTCommand "$" $ unknown]
+
+fixName = map fixChar
+fixChar c | isLetter c = c
+ | otherwise = '_'
+
+groupByFirstChar :: [ParseTree] -> [(Char, [ParseTree])]
+groupByFirstChar = MM.assocs . MM.fromList . map breakCmd
+ where
+ breakCmd (PTCommand (c:cs) params) = (c, PTCommand cs params)
+
+makePT cmd@(Command n p) = PTCommand n cmd
+
+buildParseTree cmds = [PTPrefix "!" $ (bpt $ map makePT cmds) ++ [unknowncmd]]
+
+bpt :: [ParseTree] -> [ParseTree]
+bpt cmds = cmdLeaf emptyNamed
+ where
+ emptyNamed = partition (\(_, (PTCommand n _:_)) -> null n) $ groupByFirstChar cmds
+ buildsub :: (Char, [ParseTree]) -> [ParseTree] -> ParseTree
+ buildsub (c, cmds) pc = let st = (bpt cmds) ++ pc in if null $ drop 1 st then maybeMerge c st else PTPrefix [c] st
+ buildsub' = flip buildsub []
+ cmdLeaf ([], assocs) = map buildsub' assocs
+ cmdLeaf ([(c, hwc:assocs1)], assocs2)
+ | null assocs1 = PTPrefix [c] [hwc] : map buildsub' assocs2
+ | otherwise = (buildsub (c, assocs1) [hwc]) : map buildsub' assocs2
+
+ maybeMerge c cmd@[PTCommand {}] = PTPrefix [c] cmd
+ maybeMerge c cmd@[PTPrefix s ss] = PTPrefix (c:s) ss
+ maybeMerge c [] = PTPrefix [c] []
+
+dumpTree = vcat . map dt
+ where
+ dt (PTPrefix s st) = text s $$ (nest (length s) $ vcat $ map dt st)
+ dt _ = char '$'
+
+renderArrays (letters, commands, handlers) = vcat $ punctuate (char '\n') [grr, l, s, c, bodies, structs, realHandlers, realHandlersArray, cmds]
+ where
+ maybeQuotes "$" = text "#0"
+ maybeQuotes "~" = text "#10"
+ maybeQuotes s = if null $ tail s then quotes $ text s else text s
+ l = text "const letters: array[0.." <> (int $ length letters - 1) <> text "] of char = "
+ <> parens (hsep . punctuate comma $ map maybeQuotes letters) <> semi
+ s = text "const commands: array[0.." <> (int $ length commands - 1) <> text "] of integer = "
+ <> parens (hsep . punctuate comma $ map text commands) <> semi
+ c = text "const handlers: array[0.." <> (int $ length fixedNames - 1) <> text "] of PHandler = "
+ <> parens (hsep . punctuate comma $ map (text . (:) '@') handlerTypes) <> semi
+ grr = text "const net2cmd: array[0.." <> (int $ length fixedNames - 1) <> text "] of TCmdType = "
+ <> parens (hsep . punctuate comma $ map (text . (++) "cmd_") $ reverse fixedNames) <> semi
+ handlerTypes = "handler__UNKNOWN_" : (map cmdParams2handlerType $ reverse sortedCmdDescriptions)
+ sortedCmdDescriptions = sort commandsDescription
+ fixedNames = map fixName handlers
+ bodies = vcat $ punctuate (char '\n') $ map handlerBody $ nub $ sort handlerTypes
+ handlerBody n = text "procedure " <> text n <> semi
+ $+$ text "begin"
+ $+$ text "end" <> semi
+ cmds = text "type TCmdType = " <> parens (hsep $ punctuate comma $ concatMap (rhentry "cmd_") $ sortedCmdDescriptions) <> semi
+ structs = vcat (map text . Set.toList . Set.fromList $ map cmdParams2record commandsDescription)
+ realHandlers = vcat $ punctuate (char '\n') $ map rh $ sortedCmdDescriptions
+ realHandlersArray = text "const handlers: array[TCmdType] of PHandler = "
+ <> parens (hsep . punctuate comma . concatMap (map ((<>) (text "PHandler") . parens) . rhentry "@handler_") $ sortedCmdDescriptions) <> semi
+
+rh cmd@(Command n p) = text "procedure handler_" <> text (fixName n) <> parens (text "var p: " <> text (cmdParams2str cmd)) <> semi
+ $+$ emptyBody $+$ if hasMany p then vcat [space, text "procedure handler_" <> text (fixName n) <> text "_s" <> parens (text "var s: TCmdParamS") <> semi
+ , emptyBody] else empty
+ where
+ emptyBody = text "begin" $+$ text "end" <> semi
+
+rhentry prefix cmd@(Command n p) = (text . (++) prefix . fixName . cmdName $ cmd)
+ : if hasMany p then [text . flip (++) "_s" . (++) prefix . fixName . cmdName $ cmd] else []
+
+pas = renderArrays $ buildTables $ buildParseTree commandsDescription
+ where
+ buildTables cmds = let (_, _, _, t1, t2, t3) = foldr walk (0, [0], -10, [], [], [[]]) cmds in (tail t1, tail t2, concat t3)
+ walk (PTCommand _ (Command n params)) (lc, s:sh, pc, tbl1, tbl2, (t3:tbl3)) =
+ (lc, 1:sh, pc - 1, "#10":tbl1, show pc:tbl2, (n:t3):tbl3)
+ walk (PTPrefix prefix cmds) l = lvldown $ foldr fpf (foldr walk (lvlup l) cmds) prefix
+ lvlup (lc, sh, pc, tbl1, tbl2, tbl3) = (lc, 0:sh, pc, tbl1, tbl2, []:tbl3)
+ lvldown (lc, s1:s2:sh, pc, tbl1, t:tbl2, t31:t32:tbl3) = (lc, s1+s2:sh, pc, tbl1, (if null t32 then "0" else show s1):tbl2, (t31 ++ t32):tbl3)
+ fpf c (lc, s:sh, pc, tbl1, tbl2, tbl3) = (lc + 1, s+1:sh, pc, [c]:tbl1, "0":tbl2, tbl3)
+
+main = do
+ putStrLn $ renderStyle style{mode = ZigZagMode, lineLength = 80} $ pas
+ --putStrLn $ renderStyle style{lineLength = 80} $ dumpTree $ buildParseTree commandsDescription