# HG changeset patch # User unc0rr # Date 1447703844 -10800 # Node ID b894922d58cc809058d6afd9ddce2102f6550587 # Parent fcbdee9cdd74e96a441fae4e7caa1d918ecb2e3c# Parent bea08a303cb72e3c50ab8114073108fee0005845 Merge default (add a bunch of FIXMEs) diff -r bea08a303cb7 -r b894922d58cc .hgignore --- a/.hgignore Mon Nov 16 20:07:21 2015 +0100 +++ b/.hgignore Mon Nov 16 22:57:24 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 + diff -r bea08a303cb7 -r b894922d58cc CMakeLists.txt --- a/CMakeLists.txt Mon Nov 16 20:07:21 2015 +0100 +++ b/CMakeLists.txt Mon Nov 16 22:57:24 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) diff -r bea08a303cb7 -r b894922d58cc QTfrontend/net/tcpBase.cpp --- a/QTfrontend/net/tcpBase.cpp Mon Nov 16 20:07:21 2015 +0100 +++ b/QTfrontend/net/tcpBase.cpp Mon Nov 16 22:57:24 2015 +0300 @@ -111,8 +111,6 @@ m_connected(false), IPCSocket(0) { - process = 0; - if(!IPCServer) { IPCServer = new QTcpServer(0); diff -r bea08a303cb7 -r b894922d58cc gameServer/OfficialServer/checker.hs diff -r bea08a303cb7 -r b894922d58cc hedgewars/ArgParsers.pas --- a/hedgewars/ArgParsers.pas Mon Nov 16 20:07:21 2015 +0100 +++ b/hedgewars/ArgParsers.pas Mon Nov 16 22:57:24 2015 +0300 @@ -121,17 +121,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 +204,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 +221,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 +318,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 +331,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 +348,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'); diff -r bea08a303cb7 -r b894922d58cc hedgewars/CMakeLists.txt --- a/hedgewars/CMakeLists.txt Mon Nov 16 20:07:21 2015 +0100 +++ b/hedgewars/CMakeLists.txt Mon Nov 16 22:57:24 2015 +0300 @@ -106,6 +106,20 @@ uGearsUtils.pas uTeams.pas + uFLAmmo.pas + uFLData.pas + uFLGameConfig.pas + uFLIPC.pas + uFLNet.pas + uFLNetProtocol.pas + uFLNetTypes.pas + uFLScripts.pas + uFLSchemes.pas + uFLTeams.pas + uFLTypes.pas + uFLUICallback.pas + uFLUtils.pas + #these interact with everything, so compile last uScript.pas ) diff -r bea08a303cb7 -r b894922d58cc hedgewars/SDLh.pas --- a/hedgewars/SDLh.pas Mon Nov 16 20:07:21 2015 +0100 +++ b/hedgewars/SDLh.pas Mon Nov 16 22:57:24 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,29 @@ (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; 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; diff -r bea08a303cb7 -r b894922d58cc hedgewars/hwLibrary.pas --- a/hedgewars/hwLibrary.pas Mon Nov 16 20:07:21 2015 +0100 +++ b/hedgewars/hwLibrary.pas Mon Nov 16 22:57:24 2015 +0300 @@ -29,65 +29,99 @@ 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 + , uFLData + , uFLTeams + , uFLScripts + , uFLSchemes + , uFLAmmo + , uFLNet + , uFLNetProtocol + , uFLUICallback + ; {$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 +153,35 @@ 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, + + // dunno what these are RunEngine, LoadLocaleWrapper, HW_versionInfo, diff -r bea08a303cb7 -r b894922d58cc hedgewars/hwengine.pas --- a/hedgewars/hwengine.pas Mon Nov 16 20:07:21 2015 +0100 +++ b/hedgewars/hwengine.pas Mon Nov 16 22:57:24 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,12 @@ {$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} /////////////////////////////////////////////////////////////////////////////// function DoTimer(Lag: LongInt): boolean; @@ -314,8 +303,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 +362,6 @@ begin if recordFileName = '' then begin - InitIPC; SendIPCAndWaitReply(_S'C'); // ask for game config end else @@ -432,8 +420,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 +445,6 @@ uStats.initModule; uStore.initModule; uRender.initModule; - uTeams.initModule; uVisualGears.initModule; uVisualGearsHandlers.initModule; uWorld.initModule; @@ -501,7 +488,6 @@ uCommands.freeModule; uVariables.freeModule; uUtils.freeModule; // closes debug file - uPhysFSLayer.freeModule; uScript.freeModule; end; @@ -515,7 +501,6 @@ begin initEverything(false); - InitIPC; IPCWaitPongEvent; TryDo(InitStepsFlags = cifRandomize, 'Some parameters not set (flags = ' + inttostr(InitStepsFlags) + ')', true); @@ -532,18 +517,21 @@ freeEverything(false); end; -{$IFDEF HWLIBRARY} -procedure RunEngine(argc: LongInt; argv: PPChar); cdecl; export; +function EngineThread(p: pointer): Longint; cdecl; export; +begin + if GameType = gmtLandPreview then + GenLandPreview() + else Game(); + + EngineThread:= 0 +end; + + +function RunEngine(argc: LongInt; argv: PPChar): Longint; cdecl; export; 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 +540,13 @@ 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 + SDL_CreateThread(@EngineThread, 'engine', nil); + RunEngine:= 0 + end end; -{$ENDIF} end. diff -r bea08a303cb7 -r b894922d58cc hedgewars/uDebug.pas --- a/hedgewars/uDebug.pas Mon Nov 16 20:07:21 2015 +0100 +++ b/hedgewars/uDebug.pas Mon Nov 16 22:57:24 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); diff -r bea08a303cb7 -r b894922d58cc hedgewars/uFLAmmo.pas --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hedgewars/uFLAmmo.pas Mon Nov 16 22:57:24 2015 +0300 @@ -0,0 +1,135 @@ +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); + end +end; + +end. diff -r bea08a303cb7 -r b894922d58cc hedgewars/uFLData.pas --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hedgewars/uFLData.pas Mon Nov 16 22:57:24 2015 +0300 @@ -0,0 +1,85 @@ +unit uFLData; +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. diff -r bea08a303cb7 -r b894922d58cc hedgewars/uFLGameConfig.pas --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hedgewars/uFLGameConfig.pas Mon Nov 16 22:57:24 2015 +0300 @@ -0,0 +1,327 @@ +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; + +implementation +uses uFLIPC, hwengine, uFLUtils, uFLTeams, uFLData, uFLSChemes, uFLAmmo, uFLUICallback; + +const + MAXCONFIGS = 5; + MAXARGS = 32; + +type + TGameConfig = record + seed: shortstring; + theme: shortstring; + script: shortstring; + scheme: TScheme; + ammo: TAmmo; + mapgen: 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; + end; + PGameConfig = ^TGameConfig; + +var + currentConfig: TGameConfig; + + +procedure sendConfig(config: PGameConfig); +var i: Longword; +begin +with config^ do +begin + case gameType of + gtPreview: begin + if script <> '' then + ipcToEngine('escript ' + script); + ipcToEngine('eseed ' + seed); + ipcToEngine('e$mapgen ' + intToStr(mapgen)); + end; + gtLocal: begin + if script <> '' then + ipcToEngine('escript ' + script); + ipcToEngine('eseed ' + seed); + ipcToEngine('e$mapgen ' + intToStr(mapgen)); + ipcToEngine('e$theme ' + theme); + + sendSchemeConfig(scheme); + + i:= 0; + while (i < 8) and (teams[i].hogsNumber > 0) do + begin + sendAmmoConfig(config^.ammo); + ipcToEngine('eammstore'); + sendTeamConfig(teams[i]); + inc(i) + end; + end; + end; + + ipcToEngine('!'); +end; +end; + +procedure queueExecution; +var pConfig: PGameConfig; + i: Longword; +begin + new(pConfig); + pConfig^:= currentConfig; + + with pConfig^ do + 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; + + RunEngine(pConfig^.argumentsNumber, @pConfig^.argv); + + sendConfig(pConfig) +end; + +procedure resetGameConfig; cdecl; +var i: Longword; +begin + with currentConfig do + begin + for i:= 0 to 7 do + teams[i].hogsNumber:= 0 + end +end; + +procedure setSeed(seed: PChar); cdecl; +begin + 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:= 1; + + queueExecution; + end; +end; + + +procedure getPreview; cdecl; +begin + with currentConfig do + begin + gameType:= gtPreview; + arguments[0]:= ''; + arguments[1]:= '--internal'; + arguments[2]:= '--landpreview'; + argumentsNumber:= 3; + + queueExecution; + end; +end; + +procedure runLocalGame; cdecl; +begin + with currentConfig do + begin + gameType:= gtLocal; + arguments[0]:= ''; + arguments[1]:= '--internal'; + arguments[2]:= '--nomusic'; + argumentsNumber:= 3; + + queueExecution; + end; +end; + +procedure tryAddTeam(teamName: PChar); cdecl; +var msg: ansistring; + i, hn, hedgehogsNumber: Longword; + team: PTeam; + 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 or reached hogs number maximum + if (i > 7) or (hedgehogsNumber >= 48) then exit; + + team:= teamByName(teamName); + if team = nil 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; + sendUI(mtRemoveTeam, @msg[1], length(msg)) + end +end; + + +procedure tryRemoveTeam(teamName: PChar); cdecl; +var msg: ansistring; + i: Longword; + tn: shortstring; +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; + + 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)); + sendUI(mtAddTeam, @msg[1], length(msg)) +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 + if scriptName <> 'Normal' then + currentConfig.script:= '/Scripts/Multiplayer/' + scriptName + '.lua' + else + currentConfig.script:= '' +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; + +end. diff -r bea08a303cb7 -r b894922d58cc hedgewars/uFLIPC.pas --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hedgewars/uFLIPC.pas Mon Nov 16 22:57:24 2015 +0300 @@ -0,0 +1,237 @@ +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 + 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. diff -r bea08a303cb7 -r b894922d58cc hedgewars/uFLNet.pas --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hedgewars/uFLNet.pas Mon Nov 16 22:57:24 2015 +0300 @@ -0,0 +1,295 @@ +unit uFLNet; +interface + +procedure connectOfficialServer; + +procedure initModule; +procedure freeModule; + +implementation +uses SDLh, uFLIPC, uFLTypes, uFLUICallback, uFLNetTypes; + +const endCmd: string = #10 + #10; + +function getNextChar: char; forward; +function getCurrChar: char; forward; +procedure sendNet(s: shortstring); forward; + +type + TNetState = (netDisconnected, netConnecting, netLoggedIn); + TParserState = record + cmd: TCmdType; + l: LongInt; + netState: TNetState; + buf: shortstring; + bufpos: byte; + end; + PHandler = procedure; + +var state: TParserState; + +// generated stuff here +const letters: array[0..206] of char = ('A', 'S', 'K', 'P', 'A', 'S', 'S', 'W', 'O', 'R', 'D', #10, 'B', 'A', 'N', 'L', 'I', 'S', 'T', #10, 'Y', 'E', #10, 'C', '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, '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', 'O', 'O', 'M', 'S', #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..206] of integer = (12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -38, 11, 7, 0, 0, 0, 0, 0, -37, 0, 0, -36, 26, 4, 0, 0, -35, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -34, 0, 0, 0, 0, 0, 0, 0, 0, -33, 3, 0, -32, 7, 0, 0, 0, 0, 0, -31, 5, 0, 0, 0, -30, 11, 0, 0, 0, 3, 0, -29, 0, 0, 0, -28, 7, 0, 0, 0, 0, 0, -27, 22, 4, 0, 0, -26, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, -25, 0, 0, 0, 0, -24, 11, 4, 0, 0, -23, 0, 0, 0, 0, 0, -22, 10, 4, 0, 0, -21, 0, 0, 0, 0, -20, 27, 18, 4, 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); + +procedure handler_ASKPASSWORD; +begin +end; + +procedure handler_BANLIST; +begin +end; + +procedure handler_BYE; +begin +end; + +procedure handler_CHAT; +begin +end; + +procedure handler_CLIENT_FLAGS; +begin +end; + +procedure handler_CONNECTED; +var data: TCmdConnectedData; +begin + sendUI(mtNetData, nil, 0); +end; + +procedure handler_EM; +begin +end; + +procedure handler_HH_NUM; +begin +end; + +procedure handler_INFO; +begin +end; + +procedure handler_JOINED; +begin +end; + +procedure handler_JOINING; +begin +end; + +procedure handler_KICKED; +begin +end; + +procedure handler_LEFT; +begin +end; + +procedure handler_LOBBY_JOINED; +begin +end; + +procedure handler_LOBBY_LEFT; +begin +end; + +procedure handler_NICK; +begin +end; + +procedure handler_NOTICE; +begin +end; + +procedure handler_PING; +begin + sendNet('PONG') +end; + +procedure handler_PROTO; +begin +end; + +procedure handler_ROOMS; +begin +end; + +procedure handler_ROUND_FINISHED; +begin +end; + +procedure handler_RUN_GAME; +begin +end; + +procedure handler_SERVER_AUTH; +begin +end; + +procedure handler_SERVER_MESSAGE; +begin +end; + +procedure handler_SERVER_VARS; +begin +end; + +procedure handler_TEAM_ACCEPTED; +begin +end; + +procedure handler_TEAM_COLOR; +begin +end; + +procedure handler_WARNING; +begin +end; + +procedure handler___UNKNOWN__; +begin + writeln('[NET] Unknown cmd'); +end; + +const handlers: array[0..28] of PHandler = (@handler___UNKNOWN__, @handler_WARNING, @handler_TEAM_COLOR, @handler_TEAM_ACCEPTED, @handler_SERVER_VARS, @handler_SERVER_MESSAGE, @handler_SERVER_AUTH, @handler_RUN_GAME, @handler_ROUND_FINISHED, @handler_ROOMS, @handler_PROTO, @handler_PING, @handler_NOTICE, @handler_NICK, @handler_LOBBY_LEFT, @handler_LOBBY_JOINED, @handler_LEFT, @handler_KICKED, @handler_JOINING, @handler_JOINED, @handler_INFO, @handler_HH_NUM, @handler_EM, @handler_CONNECTED, @handler_CLIENT_FLAGS, @handler_CHAT, @handler_BYE, @handler_BANLIST, @handler_ASKPASSWORD); + + +// end of generated stuff +procedure handleTail; +var cnt: Longint; + c: char; +begin + state.l:= 0; + + 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) +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 netWriter(sock: PTCPSocket): LongInt; cdecl; export; +begin + netWriter:= 0; +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); + + SDL_CreateThread(@netWriter, 'netWriter', sock); + + repeat + c:= getNextChar; + //writeln('>>>>> ', c, ' [', letters[state.l], '] ', commands[state.l]); + if c = #0 then + state.netState:= netDisconnected + 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:= TCmdType(-10 - commands[state.l]); + writeln('[NET] ', state.cmd); + handlers[-10 - commands[state.l]](); + handleTail() + end + else + inc(state.l) + else + begin + handler___UNKNOWN__(); + handleTail() + end + end + until state.netState = netDisconnected; + + sock:= nil; + + writeln('[NET] netReader: disconnected'); +end; + +procedure sendNet(s: shortstring); +begin + writeln('[NET] Send: ', s); + ipcToNet(s + endCmd); +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; + state.netState:= netConnecting; + + netReaderThread:= SDL_CreateThread(@netReader, 'netReader', nil); +end; + +procedure initModule; +begin + sock:= nil; + + SDLNet_Init; + + registerNetCallback(nil, @netSendCallback); +end; + +procedure freeModule; +begin +end; + +end. diff -r bea08a303cb7 -r b894922d58cc hedgewars/uFLNetProtocol.pas --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hedgewars/uFLNetProtocol.pas Mon Nov 16 22:57:24 2015 +0300 @@ -0,0 +1,14 @@ +unit uFLNetProtocol; +interface + +procedure passNetData(p: pointer); cdecl; + +implementation +uses uFLNetTypes; + +procedure passNetData(p: pointer); cdecl; +begin + +end; + +end. diff -r bea08a303cb7 -r b894922d58cc hedgewars/uFLNetTypes.pas --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hedgewars/uFLNetTypes.pas Mon Nov 16 22:57:24 2015 +0300 @@ -0,0 +1,16 @@ +unit uFLNetTypes; +interface + +type TCmdType = (cmd___UNKNOWN__, cmd_WARNING, cmd_TEAM_COLOR, cmd_TEAM_ACCEPTED, cmd_SERVER_VARS, cmd_SERVER_MESSAGE, cmd_SERVER_AUTH, cmd_RUN_GAME, cmd_ROUND_FINISHED, cmd_ROOMS, 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_EM, cmd_CONNECTED, cmd_CLIENT_FLAGS, cmd_CHAT, cmd_BYE, cmd_BANLIST, cmd_ASKPASSWORD); + TCmdConnectedData = record + cmd: TCmdType; + protocolNumber: Longword + end; + TCmdData = record + case byte of + 0: (cmdConnected: TCmdConnectedData) + end; + +implementation + +end. diff -r bea08a303cb7 -r b894922d58cc hedgewars/uFLSchemes.pas --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hedgewars/uFLSchemes.pas Mon Nov 16 22:57:24 2015 +0300 @@ -0,0 +1,202 @@ +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 .. 16] 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) + ); +const bools: array[0 .. 19] 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) + ); + + +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. diff -r bea08a303cb7 -r b894922d58cc hedgewars/uFLScripts.pas --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hedgewars/uFLScripts.pas Mon Nov 16 22:57:24 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. diff -r bea08a303cb7 -r b894922d58cc hedgewars/uFLTeams.pas --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hedgewars/uFLTeams.pas Mon Nov 16 22:57:24 2015 +0300 @@ -0,0 +1,182 @@ +unit uFLTeams; +interface +uses uFLTypes; + +function createRandomTeam: TTeam; +procedure sendTeamConfig(var team: TTeam); + +function getTeamsList: PPChar; cdecl; +procedure freeTeamsList; + +function teamByName(s: shortstring): PTeam; + +implementation +uses uFLUtils, uFLIPC, uPhysFSLayer, uFLData; + +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 ' + 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:= 0 + else if l[1] = '[' then + section:= -1 + else if section = 0 then + begin // [Team] + if copy(l, 1, 5) = 'Name=' then + team.teamName:= midStr(l, 6) + else if copy(l, 1, 6) = 'Grave=' then + team.graveName:= midStr(l, 7) + else if copy(l, 1, 5) = 'Fort=' then + team.fortName:= midStr(l, 6) + else if copy(l, 1, 5) = 'Flag=' then + team.flag:= midStr(l, 6) + end; + // TODO: load hedgehogs and other stuff + team.botLevel:= 1 + 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; + +end. diff -r bea08a303cb7 -r b894922d58cc hedgewars/uFLTypes.pas --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hedgewars/uFLTypes.pas Mon Nov 16 22:57:24 2015 +0300 @@ -0,0 +1,86 @@ +unit uFLTypes; +interface + +type + TMessageType = (mtPreview, mtAddPlayingTeam, mtRemovePlayingTeam, mtAddTeam, mtRemoveTeam + , mtTeamColor, mtNetData); + + 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: shortstring; + flag: shortstring; + graveName: shortstring; + fortName: shortstring; + 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 + , bottomborder: boolean; + damagefactor + , turntime + , health + , suddendeath + , caseprobability + , minestime + , landadds + , minedudpct + , explosives + , minesnum + , healthprobability + , healthcaseamount + , waterrise + , healthdecrease + , ropepct + , getawaytime + , worldedge: LongInt + end; + PScheme = ^TScheme; + TAmmo = record + ammoName: shortstring; + a, b, c, d: shortstring; + end; + PAmmo = ^TAmmo; + +implementation + +end. diff -r bea08a303cb7 -r b894922d58cc hedgewars/uFLUICallback.pas --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hedgewars/uFLUICallback.pas Mon Nov 16 22:57:24 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. diff -r bea08a303cb7 -r b894922d58cc hedgewars/uFLUtils.pas --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hedgewars/uFLUtils.pas Mon Nov 16 22:57:24 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. diff -r bea08a303cb7 -r b894922d58cc hedgewars/uIO.pas --- a/hedgewars/uIO.pas Mon Nov 16 20:07:21 2015 +0100 +++ b/hedgewars/uIO.pas Mon Nov 16 22:57:24 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. diff -r bea08a303cb7 -r b894922d58cc hedgewars/uInputHandler.pas diff -r bea08a303cb7 -r b894922d58cc hedgewars/uLocale.pas --- a/hedgewars/uLocale.pas Mon Nov 16 20:07:21 2015 +0100 +++ b/hedgewars/uLocale.pas Mon Nov 16 22:57:24 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} diff -r bea08a303cb7 -r b894922d58cc hedgewars/uMisc.pas --- a/hedgewars/uMisc.pas Mon Nov 16 20:07:21 2015 +0100 +++ b/hedgewars/uMisc.pas Mon Nov 16 22:57:24 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 diff -r bea08a303cb7 -r b894922d58cc hedgewars/uPhysFSLayer.pas --- a/hedgewars/uPhysFSLayer.pas Mon Nov 16 20:07:21 2015 +0100 +++ b/hedgewars/uPhysFSLayer.pas Mon Nov 16 22:57:24 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))); diff -r bea08a303cb7 -r b894922d58cc hedgewars/uRender.pas diff -r bea08a303cb7 -r b894922d58cc hedgewars/uTypes.pas --- a/hedgewars/uTypes.pas Mon Nov 16 20:07:21 2015 +0100 +++ b/hedgewars/uTypes.pas Mon Nov 16 22:57:24 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, diff -r bea08a303cb7 -r b894922d58cc hedgewars/uUtils.pas --- a/hedgewars/uUtils.pas Mon Nov 16 20:07:21 2015 +0100 +++ b/hedgewars/uUtils.pas Mon Nov 16 22:57:24 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+} diff -r bea08a303cb7 -r b894922d58cc hedgewars/uVariables.pas --- a/hedgewars/uVariables.pas Mon Nov 16 20:07:21 2015 +0100 +++ b/hedgewars/uVariables.pas Mon Nov 16 22:57:24 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 := ''; diff -r bea08a303cb7 -r b894922d58cc misc/OfficialChallenges/racer_#17.hwmap --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/misc/OfficialChallenges/racer_#17.hwmap Mon Nov 16 22:57:24 2015 +0300 @@ -0,0 +1,1 @@ +AAAA4Xic43vHWMfAc4SFi4FrB5DFrsEiy8DCyriegTGGRYeBIYldlIGfkX02A/di9qIO7sWsrxhARMf/K0AWLy9bUwffXiDBEst2qPP/b3bRzQwO7FIXGO3YpUAGmC0GGrVlMbsui9JiEPcSkLgDMn7LJRZWpkwGoIT6JSDxn4HvCWPvZq7NjHWbQc64wX2FSYmB7yVj8w0+VqZ0Bp6TLAybgY5kvcFzhOk0AwMDu+gmkNMuMFYC3ceSw3bhApM7u0AXgwgjbyvDJwZ9Bj5p9tBW/nvsoQwAuyw7Aw== \ No newline at end of file diff -r bea08a303cb7 -r b894922d58cc qmlFrontend/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qmlFrontend/CMakeLists.txt Mon Nov 16 22:57:24 2015 +0300 @@ -0,0 +1,26 @@ +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) diff -r bea08a303cb7 -r b894922d58cc qmlFrontend/flib.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qmlFrontend/flib.h Mon Nov 16 22:57:24 2015 +0300 @@ -0,0 +1,66 @@ +#ifndef FLIB_H +#define FLIB_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +enum MessageType { + MSG_PREVIEW + , MSG_ADDPLAYINGTEAM + , MSG_REMOVEPLAYINGTEAM + , MSG_ADDTEAM + , MSG_REMOVETEAM + , MSG_TEAMCOLOR + , MSG_NETDATA +}; + +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 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 diff -r bea08a303cb7 -r b894922d58cc qmlFrontend/hwengine.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qmlFrontend/hwengine.cpp Mon Nov 16 22:57:24 2015 +0300 @@ -0,0 +1,272 @@ +#include +#include +#include +#include +#include + +#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; +} + +Q_DECLARE_METATYPE(MessageType); + +HWEngine::HWEngine(QQmlEngine *engine, QObject *parent) : + QObject(parent), + m_engine(engine) +{ + qRegisterMetaType("MessageType"); + + QLibrary hwlib("./libhwengine.so"); + + 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"); + + 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("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" << b.size() << b; + + 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_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(msg); + break; + } + case MSG_ADDTEAM: { + emit localTeamAdded(msg, 0); + break; + } + case MSG_REMOVETEAM: { + emit localTeamRemoved(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_NETDATA: { + flibPassNetData(msg.constData()); + } + } +} + +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::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()); +} diff -r bea08a303cb7 -r b894922d58cc qmlFrontend/hwengine.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qmlFrontend/hwengine.h Mon Nov 16 22:57:24 2015 +0300 @@ -0,0 +1,62 @@ +#ifndef HWENGINE_H +#define HWENGINE_H + +#include +#include +#include +#include + +#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); + +signals: + 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); + +public slots: + +private: + QQmlEngine * m_engine; + + 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 + diff -r bea08a303cb7 -r b894922d58cc qmlFrontend/main.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qmlFrontend/main.cpp Mon Nov 16 22:57:24 2015 +0300 @@ -0,0 +1,27 @@ +#include +#include + +#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(); +} diff -r bea08a303cb7 -r b894922d58cc qmlFrontend/previewimageprovider.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qmlFrontend/previewimageprovider.cpp Mon Nov 16 22:57:24 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 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); +} diff -r bea08a303cb7 -r b894922d58cc qmlFrontend/previewimageprovider.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qmlFrontend/previewimageprovider.h Mon Nov 16 22:57:24 2015 +0300 @@ -0,0 +1,21 @@ +#ifndef PREVIEWIMAGEPROVIDER_H +#define PREVIEWIMAGEPROVIDER_H + +#include +#include +#include + +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 diff -r bea08a303cb7 -r b894922d58cc qmlFrontend/qml/qmlFrontend/Connect.qml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qmlFrontend/qml/qmlFrontend/Connect.qml Mon Nov 16 22:57:24 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); + } +} diff -r bea08a303cb7 -r b894922d58cc qmlFrontend/qml/qmlFrontend/First.qml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qmlFrontend/qml/qmlFrontend/First.qml Mon Nov 16 22:57:24 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 + } +} diff -r bea08a303cb7 -r b894922d58cc qmlFrontend/qml/qmlFrontend/GameConfig.qml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qmlFrontend/qml/qmlFrontend/GameConfig.qml Mon Nov 16 22:57:24 2015 +0300 @@ -0,0 +1,298 @@ +import QtQuick 2.0 +import Hedgewars.Engine 1.0 + + +Rectangle { + HWButton { + id: btnPreview + x: 50 + y: 16 + width: 256 + height: 128 + + onClicked: HWEngine.getPreview() + + Connections { + target: HWEngine + onPreviewImageChanged: previewImage.source = "image://preview/" + HWEngine.currentSeed() + } + + Image { + id: previewImage + x: 0 + y: 0 + width: 256 + height: 128 + cache: false + } + } + + HWButton { + id: btnRunGame + x: 600 + y: 440 + width: 32 + height: 32 + + onClicked: HWEngine.runLocalGame() + } + + HWComboBox { + id: cbTheme + x: 50 + y: 160 + 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) + } + } + } + } + + HWComboBox { + id: cbScript + x: 50 + y: 256 + width: 256 + height: 64 + + 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) + } + } + } + } + + HWComboBox { + id: cbScheme + x: 50 + y: 336 + width: 256 + height: 64 + + 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) + } + } + } + } + + + HWComboBox { + id: cbAmmo + x: 50 + y: 416 + width: 256 + height: 64 + + 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) + } + } + } + } + + 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) + } + } + } + + + } + + Connections { + target: HWEngine + onPlayingTeamAdded: playingTeamsModel.append({ + "aiLevel": aiLevel + , "name": teamName + , "local": isLocal + , "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) + } + } + } + + 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() + } +} diff -r bea08a303cb7 -r b894922d58cc qmlFrontend/qml/qmlFrontend/HWButton.qml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qmlFrontend/qml/qmlFrontend/HWButton.qml Mon Nov 16 22:57:24 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" + } + }] +} diff -r bea08a303cb7 -r b894922d58cc qmlFrontend/qml/qmlFrontend/HWComboBox.qml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qmlFrontend/qml/qmlFrontend/HWComboBox.qml Mon Nov 16 22:57:24 2015 +0300 @@ -0,0 +1,57 @@ +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 + } + } + + onClicked: selection.visibility = Window.Windowed +} diff -r bea08a303cb7 -r b894922d58cc qmlFrontend/qml/qmlFrontend/LocalGame.qml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qmlFrontend/qml/qmlFrontend/LocalGame.qml Mon Nov 16 22:57:24 2015 +0300 @@ -0,0 +1,24 @@ +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 = "GameConfig" + } +} diff -r bea08a303cb7 -r b894922d58cc qmlFrontend/qml/qmlFrontend/main.qml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qmlFrontend/qml/qmlFrontend/main.qml Mon Nov 16 22:57:24 2015 +0300 @@ -0,0 +1,36 @@ +import QtQuick 2.0 + +Rectangle { + id: pages + width: 800 + height: 600 + + property variant pagesList : [ + "First" + , "LocalGame" + , "GameConfig" + , "Connect" + ]; + + property string currentPage : "First"; + + Repeater { + model: pagesList; + + delegate: Loader { + active: false + asynchronous: true + anchors.fill: parent + visible: (currentPage === modelData) + source: "%1.qml".arg(modelData) + onVisibleChanged: loadIfNotLoaded(); + Component.onCompleted: loadIfNotLoaded(); + + function loadIfNotLoaded () + { + if (visible && !active) + active = true; + } + } + } +} diff -r bea08a303cb7 -r b894922d58cc qmlFrontend/qmlFrontend.qrc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qmlFrontend/qmlFrontend.qrc Mon Nov 16 22:57:24 2015 +0300 @@ -0,0 +1,11 @@ + + + qml/qmlFrontend/First.qml + qml/qmlFrontend/GameConfig.qml + qml/qmlFrontend/HWButton.qml + qml/qmlFrontend/HWComboBox.qml + qml/qmlFrontend/LocalGame.qml + qml/qmlFrontend/main.qml + qml/qmlFrontend/Connect.qml + + diff -r bea08a303cb7 -r b894922d58cc qmlFrontend/qtquick2applicationviewer/qtquick2applicationviewer.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qmlFrontend/qtquick2applicationviewer/qtquick2applicationviewer.cpp Mon Nov 16 22:57:24 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 +#include +#include + +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 +} diff -r bea08a303cb7 -r b894922d58cc qmlFrontend/qtquick2applicationviewer/qtquick2applicationviewer.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qmlFrontend/qtquick2applicationviewer/qtquick2applicationviewer.h Mon Nov 16 22:57:24 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 + +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 diff -r bea08a303cb7 -r b894922d58cc qmlFrontend/qtquick2applicationviewer/qtquick2applicationviewer.pri --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qmlFrontend/qtquick2applicationviewer/qtquick2applicationviewer.pri Mon Nov 16 22:57:24 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) +} diff -r bea08a303cb7 -r b894922d58cc qmlFrontend/themeiconprovider.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qmlFrontend/themeiconprovider.cpp Mon Nov 16 22:57:24 2015 +0300 @@ -0,0 +1,38 @@ +#include +#include + +#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 = QImage::fromData(buf); + + if (size) + *size = img.size(); + return img; +} diff -r bea08a303cb7 -r b894922d58cc qmlFrontend/themeiconprovider.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qmlFrontend/themeiconprovider.h Mon Nov 16 22:57:24 2015 +0300 @@ -0,0 +1,21 @@ +#ifndef THEMEICONPROVIDER_H +#define THEMEICONPROVIDER_H + +#include +#include + +#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 diff -r bea08a303cb7 -r b894922d58cc tools/protocolParser.hs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/protocolParser.hs Mon Nov 16 22:57:24 2015 +0300 @@ -0,0 +1,182 @@ +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] + +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] +data ClientStates = NotConnected + | JustConnected + | ServerAuth + | Lobby + +data ParseTree = PTPrefix String [ParseTree] + | PTCommand String HWProtocol + +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 + , cmd1 "HH_NUM" $ Many [SS] + , cmd1 "TEAM_COLOR" $ Many [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 $ Many [SS] + , 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 "ROOMS" $ Many [SS] + , cmd "KICKED" [] + , cmd "RUN_GAME" [] + , cmd "ROUND_FINISHED" [] + ] + +unknowncmd = PTPrefix "$" [PTCommand "$" $ Command "__UNKNOWN__" [Many [SS]]] + +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 cmds = if not . null $ fst emptyNamed then cmdLeaf emptyNamed else subtree + where + emptyNamed = partition (\(_, (PTCommand n _:_)) -> null n) assocs + assocs = groupByFirstChar cmds + subtree = map buildsub assocs + buildsub (c, cmds) = let st = bpt cmds in if null $ drop 1 st then maybeMerge c st else PTPrefix [c] st + maybeMerge c cmd@[PTCommand {}] = PTPrefix [c] cmd + maybeMerge c cmd@[PTPrefix s ss] = PTPrefix (c:s) ss + cmdLeaf ([(c, (hwc:_))], assocs2) = (PTPrefix [c] [hwc]) : map buildsub assocs2 + +dumpTree = vcat . map dt + where + dt (PTPrefix s st) = text s $$ (nest 1 $ vcat $ map dt st) + dt _ = empty + +pas2 = buildSwitch $ buildParseTree commandsDescription + where + buildSwitch cmds = text "case getNextChar of" $$ (nest 4 . vcat $ map buildCase cmds) $$ elsePart + buildCase (PTCommand {}) = text "#10: ;" + buildCase (PTPrefix (s:ss) cmds) = quotes (char s) <> text ": " <> consumePrefix ss (buildSwitch cmds) + consumePrefix "" = id + consumePrefix str = (text "consume" <> (parens . quotes $ text str) <> semi $$) + zeroChar = text "#0: state:= pstDisconnected;" + elsePart = text "else end;" + +renderArrays (letters, commands, handlers) = vcat $ punctuate (char '\n') [cmds, l, s, {-bodies, -}c, structs, realHandlers, realHandlersArray] + where + maybeQuotes "$" = text "#0" + 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 + handlerTypes = map cmdParams2handlerType sortedCmdDescriptions + sortedCmdDescriptions = reverse $ sort commandsDescription + fixedNames = map fixName handlers + bodies = vcat $ punctuate (char '\n') $ map handlerBody fixedNames + handlerBody n = text "procedure handler_" <> text n <> semi + $+$ text "begin" + $+$ text "end" <> semi + cmds = text "type TCmdType = " <> parens (hsep $ punctuate comma $ map ((<>) (text "cmd_") . text) $ reverse fixedNames) <> 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 . (:) (text "@handler__UNKNOWN_") $ map (text . (++) "@handler_" . fixName . cmdName) sortedCmdDescriptions) <> semi + +rh cmd@(Command n p) = text "procedure handler_" <> text (fixName n) <> parens (text "var p: " <> text (cmdParams2str cmd)) <> semi + $+$ emptyBody $+$ if hasMany then vcat [space, text "procedure handler_" <> text (fixName n) <> text "_s" <> parens (text "var s: shortstring") <> semi + , emptyBody] else empty + where + hasMany = any isMany p + isMany (Many _) = True + isMany _ = False + emptyBody = text "begin" $+$ text "end" <> semi + +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 = + putStrLn $ renderStyle style{lineLength = 80} $ pas + --putStrLn $ renderStyle style{lineLength = 80} $ dumpTree $ buildParseTree commandsDescription