# HG changeset patch # User unc0rr # Date 1468611547 -10800 # Node ID 6adf27e85caeb852fdb234f5bab35a89aff226fd # Parent a69124eb7ce77f2922b78761b12162fd3e06f699# Parent 24f309d75da88fc638776e6727f640f50a5607f7 merge default diff -r 24f309d75da8 -r 6adf27e85cae .hgignore --- a/.hgignore Sat Jul 09 22:40:44 2016 +0300 +++ b/.hgignore Fri Jul 15 22:39:07 2016 +0300 @@ -53,7 +53,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 @@ -63,6 +62,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 @@ -78,3 +80,4 @@ glob:xcuserdata glob:*.mode1v3 glob:*.mode2v3 + diff -r 24f309d75da8 -r 6adf27e85cae CMakeLists.txt --- a/CMakeLists.txt Sat Jul 09 22:40:44 2016 +0300 +++ b/CMakeLists.txt Fri Jul 15 22:39:07 2016 +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) @@ -256,7 +256,7 @@ else(ANDROID) #skip frontend for javascript if(NOT BUILD_ENGINE_JS) - add_subdirectory(QTfrontend) + add_subdirectory(qmlFrontend) add_subdirectory(share) endif() diff -r 24f309d75da8 -r 6adf27e85cae QTfrontend/net/tcpBase.cpp --- a/QTfrontend/net/tcpBase.cpp Sat Jul 09 22:40:44 2016 +0300 +++ b/QTfrontend/net/tcpBase.cpp Fri Jul 15 22:39:07 2016 +0300 @@ -111,8 +111,6 @@ m_connected(false), IPCSocket(0) { - process = 0; - if(!IPCServer) { IPCServer = new QTcpServer(0); diff -r 24f309d75da8 -r 6adf27e85cae gameServer/OfficialServer/checker.hs diff -r 24f309d75da8 -r 6adf27e85cae hedgewars/ArgParsers.pas --- a/hedgewars/ArgParsers.pas Sat Jul 09 22:40:44 2016 +0300 +++ b/hedgewars/ArgParsers.pas Fri Jul 15 22:39:07 2016 +0300 @@ -22,21 +22,24 @@ interface procedure GetParams; -{$IFDEF HWLIBRARY} -var operatingsystem_parameter_argc: LongInt = 0; {$IFNDEF PAS2C}{$IFNDEF IPHONEOS}cdecl;{$ENDIF} export;{$ENDIF} - operatingsystem_parameter_argv: pointer = nil; {$IFNDEF PAS2C}{$IFNDEF IPHONEOS}cdecl;{$ENDIF} export;{$ENDIF} - operatingsystem_parameter_envp: pointer = nil; {$IFNDEF PAS2C}{$IFNDEF IPHONEOS}cdecl;{$ENDIF} export;{$ENDIF} + +{$IFNDEF BSD} +var operatingsystem_parameter_argc: NativeInt; external; + operatingsystem_parameter_argv: pointer; external; + operatingsystem_parameter_envp: pointer; external; +{$ELSE} +var operatingsystem_parameter_argc: LongInt; export; + operatingsystem_parameter_argv: pointer; export; + operatingsystem_parameter_envp: pointer; export; +{$ENDIF} function ParamCount: LongInt; function ParamStr(i: LongInt): shortstring; -{$ENDIF} implementation uses uVariables, uTypes, uUtils, uSound, uConsts; var isInternal: Boolean; -{$IFDEF HWLIBRARY} - type PCharArray = array[0..255] of PChar; PPCharArray = ^PCharArray; @@ -50,8 +53,6 @@ ParamStr:= StrPas(PPCharArray(operatingsystem_parameter_argv)^[i]) end; -{$ENDIF} - procedure GciEasterEgg; begin WriteLn(stdout, ' '); @@ -121,17 +122,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 +205,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 +222,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 +319,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 +332,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 +349,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 24f309d75da8 -r 6adf27e85cae hedgewars/CMakeLists.txt --- a/hedgewars/CMakeLists.txt Sat Jul 09 22:40:44 2016 +0300 +++ b/hedgewars/CMakeLists.txt Fri Jul 15 22:39:07 2016 +0300 @@ -106,6 +106,22 @@ uGearsUtils.pas uTeams.pas + uFLAmmo.pas + uFLDrawnMap.pas + uFLGameConfig.pas + uFLIPC.pas + uFLNet.pas + uFLNetProtocol.pas + uFLNetTypes.pas + uFLRunQueue.pas + uFLScripts.pas + uFLSchemes.pas + uFLTeams.pas + uFLThemes.pas + uFLTypes.pas + uFLUICallback.pas + uFLUtils.pas + #these interact with everything, so compile last uScript.pas ) diff -r 24f309d75da8 -r 6adf27e85cae hedgewars/SDLh.pas --- a/hedgewars/SDLh.pas Sat Jul 09 22:40:44 2016 +0300 +++ b/hedgewars/SDLh.pas Fri Jul 15 22:39:07 2016 +0300 @@ -918,6 +918,8 @@ PSDL_Thread = Pointer; PSDL_mutex = Pointer; + PSDL_sem = Pointer; + PSDL_cond = Pointer; TSDL_GLattr = ( SDL_GL_RED_SIZE, @@ -1143,13 +1145,30 @@ function SDL_CreateThread(fn: Pointer; name: PChar; data: Pointer): PSDL_Thread; cdecl; external SDLLibName; {$ENDIF} procedure SDL_WaitThread(thread: PSDL_Thread; status: PLongInt); cdecl; external SDLLibName; -procedure SDL_KillThread(thread: PSDL_Thread); cdecl; external SDLLibName; +procedure SDL_DetachThread(thread: PSDL_Thread); cdecl; external SDLLibName; function SDL_CreateMutex: PSDL_mutex; cdecl; external SDLLibName; procedure SDL_DestroyMutex(mutex: PSDL_mutex); cdecl; external SDLLibName; function SDL_LockMutex(mutex: PSDL_mutex): LongInt; cdecl; external SDLLibName; function SDL_UnlockMutex(mutex: PSDL_mutex): LongInt; cdecl; external SDLLibName; +function SDL_CreateCond: PSDL_cond; cdecl; external SDLLibName; +procedure SDL_DestroyCond(cond: PSDL_cond); cdecl; external SDLLibName; +function SDL_CondSignal(cond: PSDL_cond): LongInt; cdecl; external SDLLibName; +function SDL_CondBroadcast(cond: PSDL_cond): LongInt; cdecl; external SDLLibName; +function SDL_CondWait(cond: PSDL_cond; mut: PSDL_mutex): LongInt; cdecl; external SDLLibName; +function SDL_CondWaitTimeout(cond: PSDL_cond; mut: PSDL_mutex; ms: Longword): LongInt; cdecl; external SDLLibName; + + +function SDL_CreateSemaphore(initial_value: Longword): PSDL_sem; cdecl; external SDLLibName; +procedure SDL_DestroySemaphore(sem: PSDL_sem); cdecl; external SDLLibName; +function SDL_SemWait(sem: PSDL_sem): LongInt; cdecl; external SDLLibName; +function SDL_SemTryWait(sem: PSDL_sem): LongInt; cdecl; external SDLLibName; +function SDL_SemWaitTimeout(sem: PSDL_sem; ms: Longword): LongInt; cdecl; external SDLLibName; +function SDL_SemPost(sem: PSDL_sem): LongInt; cdecl; external SDLLibName; +function SDL_SemValue(sem: PSDL_sem): Longword; cdecl; external SDLLibName; + + function SDL_GL_SetAttribute(attr: TSDL_GLattr; value: LongInt): LongInt; cdecl; external SDLLibName; procedure SDL_GL_SwapBuffers; cdecl; external SDLLibName; diff -r 24f309d75da8 -r 6adf27e85cae hedgewars/hwLibrary.pas --- a/hedgewars/hwLibrary.pas Sat Jul 09 22:40:44 2016 +0300 +++ b/hedgewars/hwLibrary.pas Fri Jul 15 22:39:07 2016 +0300 @@ -29,65 +29,100 @@ Library hwLibrary; -uses hwengine, uTypes, uConsts, uVariables, uSound, uCommands, uUtils, - uLocale{$IFDEF ANDROID}, jni{$ENDIF}; +uses hwengine + , uTypes + , uConsts + , uVariables + , uSound + , uCommands + , uUtils + , uLocale + {$IFDEF ANDROID}, jni{$ENDIF} + , uFLTypes + , uFLGameConfig + , uFLIPC + , uPhysFSLayer + , uFLThemes + , uFLTeams + , uFLScripts + , uFLSchemes + , uFLAmmo + , uFLNet + , uFLNetProtocol + , uFLUICallback + , uFLRunQueue + ; {$INCLUDE "config.inc"} // retrieve protocol information -procedure HW_versionInfo(netProto: PLongInt; versionStr: PPChar); cdecl; export; +procedure HW_versionInfo(netProto: PLongInt; versionStr: PPChar); cdecl; begin netProto^:= cNetProtoVersion; versionStr^:= cVersionString; end; -function HW_versionString: PChar; cdecl; export; +function HW_versionString: PChar; cdecl; begin exit(cVersionString + '-r' + cRevisionString + ' (' + cHashString + ')'); end; // equivalent to esc+y; when closeFrontend = true the game exits after memory cleanup -procedure HW_terminate(closeFrontend: boolean); cdecl; export; +procedure HW_terminate(closeFrontend: boolean); cdecl; begin closeFrontend:= closeFrontend; // avoid hint ParseCommand('forcequit', true); end; -function HW_getWeaponNameByIndex(whichone: LongInt): PChar; cdecl; export; +function HW_getWeaponNameByIndex(whichone: LongInt): PChar; cdecl; begin HW_getWeaponNameByIndex:= (str2pchar(trammo[Ammoz[TAmmoType(whichone+1)].NameId])); end; -(*function HW_getWeaponCaptionByIndex(whichone: LongInt): PChar; cdecl; export; +(*function HW_getWeaponCaptionByIndex(whichone: LongInt): PChar; cdecl; begin HW_getWeaponCaptionByIndex:= (str2pchar(trammoc[Ammoz[TAmmoType(whichone+1)].NameId])); end; -function HW_getWeaponDescriptionByIndex(whichone: LongInt): PChar; cdecl; export; +function HW_getWeaponDescriptionByIndex(whichone: LongInt): PChar; cdecl; begin HW_getWeaponDescriptionByIndex:= (str2pchar(trammod[Ammoz[TAmmoType(whichone+1)].NameId])); end;*) -function HW_getNumberOfWeapons: LongInt; cdecl; export; +function HW_getNumberOfWeapons: LongInt; cdecl; begin HW_getNumberOfWeapons:= ord(high(TAmmoType)); end; -function HW_getMaxNumberOfHogs: LongInt; cdecl; export; +function HW_getMaxNumberOfHogs: LongInt; cdecl; begin HW_getMaxNumberOfHogs:= cMaxHHIndex + 1; end; -function HW_getMaxNumberOfTeams: LongInt; cdecl; export; +function HW_getMaxNumberOfTeams: LongInt; cdecl; begin HW_getMaxNumberOfTeams:= cMaxTeams; end; -procedure HW_memoryWarningCallback; cdecl; export; +procedure HW_memoryWarningCallback; cdecl; begin ReleaseSound(false); end; +procedure flibInit(localPrefix, userPrefix: PChar); cdecl; +begin + initIPC; + uPhysFSLayer.initModule(localPrefix, userPrefix); + uFLNet.initModule; +end; + +procedure flibFree; cdecl; +begin + uFLNet.freeModule; + uPhysFSLayer.freemodule; + freeIPC; +end; + {$IFDEF ANDROID} function JNI_HW_versionInfoNet(env: PJNIEnv; obj: JObject):JInt;cdecl; begin @@ -119,6 +154,39 @@ Game; {$ELSE} exports + runQuickGame, + runLocalGame, + getPreview, + registerUIMessagesCallback, + flibInit, + flibFree, + //game config + resetGameConfig, + setSeed, + getSeed, + setTheme, + setScript, + setScheme, + setAmmo, + getThemesList, + freeThemesList, + getThemeIcon, + getScriptsList, + getSchemesList, + getAmmosList, + getTeamsList, + tryAddTeam, + tryRemoveTeam, + changeTeamColor, + // network + connectOfficialServer, + passNetData, + passFlibEvent, + sendChatLine, + joinRoom, + partRoom, + + // dunno what these are RunEngine, LoadLocaleWrapper, HW_versionInfo, diff -r 24f309d75da8 -r 6adf27e85cae hedgewars/hwengine.pas --- a/hedgewars/hwengine.pas Sat Jul 09 22:40:44 2016 +0300 +++ b/hedgewars/hwengine.pas Fri Jul 15 22:39:07 2016 +0300 @@ -22,12 +22,8 @@ {$R res/hwengine.rc} {$ENDIF} -{$IFDEF HWLIBRARY} unit hwengine; interface -{$ELSE} -program hwengine; -{$ENDIF} uses {$IFDEF IPHONEOS}cmem, {$ENDIF} SDLh, uMisc, uConsole, uGame, uConsts, uLand, uAmmos, uVisualGears, uGears, uStore, uWorld, uInputHandler , uSound, uScript, uTeams, uStats, uIO, uLocale, uChat, uAI, uAIMisc, uAILandMarks, uLandTexture, uCollisions @@ -38,19 +34,13 @@ {$IFDEF ANDROID}, GLUnit{$ENDIF} ; -{$IFDEF HWLIBRARY} -function RunEngine(argc: LongInt; argv: PPChar): LongInt; cdecl; export; - +function RunEngine(argc: LongInt; argv: PPChar): Longint; cdecl; export; procedure preInitEverything(); procedure initEverything(complete:boolean); procedure freeEverything(complete:boolean); implementation -{$ELSE} -procedure preInitEverything(); forward; -procedure initEverything(complete:boolean); forward; -procedure freeEverything(complete:boolean); forward; -{$ENDIF} +uses uFLUICallback, uFLTypes; /////////////////////////////////////////////////////////////////////////////// function DoTimer(Lag: LongInt): boolean; @@ -336,8 +326,8 @@ begin 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)); @@ -398,7 +388,6 @@ begin if recordFileName = '' then begin - InitIPC; SendIPCAndWaitReply(_S'C'); // ask for game config end else @@ -438,6 +427,7 @@ procedure Game; begin initEverything(true); + SendIPC('TG'); GameRoutine; // clean up all the memory allocated freeEverything(true); @@ -457,12 +447,6 @@ procedure initEverything (complete:boolean); begin - PathPrefix:= PathPrefix + #0; - UserPathPrefix:= UserPathPrefix + #0; - uPhysFSLayer.initModule(@PathPrefix[1], @UserPathPrefix[1]); - PathPrefix:= copy(PathPrefix, 1, length(PathPrefix) - 1); - UserPathPrefix:= copy(UserPathPrefix, 1, length(UserPathPrefix) - 1); - uUtils.initModule(complete); // opens the debug file, must be the first uVariables.initModule; // inits all global variables uCommands.initModule; // helps below @@ -473,6 +457,7 @@ uIO.initModule; // sets up sockets uScript.initModule; + uTeams.initModule; // clear CurrentTeam variable if complete then begin @@ -496,7 +481,6 @@ uStats.initModule; uStore.initModule; uRender.initModule; - uTeams.initModule; uVisualGears.initModule; uVisualGearsHandlers.initModule; uWorld.initModule; @@ -540,7 +524,6 @@ uCommands.freeModule; uVariables.freeModule; uUtils.freeModule; // closes debug file - uPhysFSLayer.freeModule; uScript.freeModule; end; @@ -554,7 +537,6 @@ begin initEverything(false); - InitIPC; if allOK then begin IPCWaitPongEvent; @@ -567,6 +549,7 @@ GenPreviewAlpha(Preview); {$ENDIF} WriteLnToConsole('Sending preview...'); + SendIPC('TP'); SendIPCRaw(@Preview, sizeof(Preview)); SendIPCRaw(@MaxHedgehogs, sizeof(byte)); WriteLnToConsole('Preview sent, disconnect'); @@ -575,18 +558,25 @@ freeEverything(false); end; -{$IFDEF HWLIBRARY} -function RunEngine(argc: LongInt; argv: PPChar): LongInt; cdecl; export; +function EngineThread(p: pointer): Longint; cdecl; export; +var e: TFLIBEvent; +begin + if GameType = gmtLandPreview then + GenLandPreview() + else Game(); + + e:= flibGameFinished; + sendUI(mtFlibEvent, @e, sizeof(e)); + EngineThread:= 0 +end; + + +function RunEngine(argc: LongInt; argv: PPChar): Longint; cdecl; export; +var t: PSDL_Thread; begin operatingsystem_parameter_argc:= argc; operatingsystem_parameter_argv:= argv; -{$ELSE} -begin -{$ENDIF} -/////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////// m a i n /////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////// {$IFDEF PAS2C} // workaround for pascal's ParamStr and ParamCount init(argc, argv); @@ -595,40 +585,14 @@ GetParams(); - if GameType = gmtLandPreview then - GenLandPreview() - else if GameType <> gmtSyntax then - Game(); - - // return 1 when engine is not called correctly if GameType = gmtSyntax then - {$IFDEF PAS2C} - exit(HaltUsageError); - {$ELSE} - halt(HaltUsageError); - {$ENDIF} - - if cTestLua then - begin - WriteLnToConsole(errmsgLuaTestTerm); - {$IFDEF PAS2C} - exit(HaltTestUnexpected); - {$ELSE} - halt(HaltTestUnexpected); - {$ENDIF} - end; - - {$IFDEF PAS2C} - exit(HaltNoError); - {$ELSE} - {$IFDEF IPHONEOS} - exit(HaltNoError); - {$ELSE} - halt(HaltNoError); - {$ENDIF} - {$ENDIF} -{$IFDEF HWLIBRARY} + RunEngine:= HaltUsageError + else + begin + t:= SDL_CreateThread(@EngineThread, 'engine', nil); + SDL_DetachThread(t); + RunEngine:= 0 + end end; -{$ENDIF} end. diff -r 24f309d75da8 -r 6adf27e85cae hedgewars/uDebug.pas --- a/hedgewars/uDebug.pas Sat Jul 09 22:40:44 2016 +0300 +++ b/hedgewars/uDebug.pas Fri Jul 15 22:39:07 2016 +0300 @@ -37,12 +37,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 24f309d75da8 -r 6adf27e85cae hedgewars/uFLAmmo.pas --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hedgewars/uFLAmmo.pas Fri Jul 15 22:39:07 2016 +0300 @@ -0,0 +1,136 @@ +unit uFLAmmo; +interface +uses uFLTypes; + +function getAmmosList: PPChar; cdecl; +procedure freeAmmosList; + +function ammoByName(s: shortstring): PAmmo; +procedure sendAmmoConfig(var ammo: TAmmo); + +implementation +uses uFLUtils, uFLIPC, uPhysFSLayer, uFLThemes; + +const MAX_AMMO_NAMES = 64; +type + TAmmoArray = array [0..0] of TAmmo; + PAmmoArray = ^TAmmoArray; +var + ammoList: PAmmo; + ammoNumber: LongInt; + listOfAmmoNames: array[0..MAX_AMMO_NAMES] of PChar; + +procedure loadAmmo; +var f: PFSFile; + ammo: PAmmo; + ammos: PAmmoArray; + s: ansistring; + i: Longword; +begin + f:= pfsOpenRead('/Config/weapons.ini'); + ammoNumber:= 0; + + if f <> nil then + begin + while (not pfsEOF(f)) do + begin + pfsReadLnA(f, s); + + if (length(s) > 0) and (s[1] <> '[') then + inc(ammoNumber); + end; + + //inc(ammoNumber); // add some default ammo + + ammoList:= GetMem(sizeof(ammoList^) * (ammoNumber + 1)); + ammo:= PAmmo(ammoList); + pfsSeek(f, 0); + + while (not pfsEOF(f)) do + begin + pfsReadLnA(f, s); + + i:= 1; + while(i <= length(s)) and (s[i] <> '=') do inc(i); + + if i < length(s) then + begin + ammo^.ammoName:= copy(s, 1, i - 1); + delete(s, 1, i); + // TODO: split into 4 shortstrings + i:= length(s) div 4; + ammo^.a:= copy(s, 1, i); + ammo^.b:= copy(s, i + 1, i); + ammo^.c:= copy(s, i * 2 + 1, i); + ammo^.d:= copy(s, i * 3 + 1, i); + inc(ammo) + end; + end; + + pfsClose(f) + end; +end; + + +function getAmmosList: PPChar; cdecl; +var i, t, l: Longword; + ammo: PAmmo; +begin + if ammoList = nil then + loadAmmo; + + t:= ammoNumber; + if t >= MAX_AMMO_NAMES then + t:= MAX_AMMO_NAMES; + + ammo:= ammoList; + for i:= 0 to Pred(t) do + begin + l:= length(ammo^.ammoName); + if l >= 255 then l:= 254; + ammo^.ammoName[l + 1]:= #0; + listOfAmmoNames[i]:= @ammo^.ammoName[1]; + inc(ammo) + end; + + listOfAmmoNames[t]:= nil; + + getAmmosList:= listOfAmmoNames +end; + +function ammoByName(s: shortstring): PAmmo; +var i: Longword; + ammo: PAmmo; +begin + ammo:= ammoList; + i:= 0; + while (i < ammoNumber) and (ammo^.ammoName <> s) do + begin + inc(ammo); + inc(i) + end; + + if i < ammoNumber then ammoByName:= ammo else ammoByName:= nil +end; + +procedure freeAmmosList; +begin + if ammoList <> nil then + FreeMem(ammoList, sizeof(ammoList^) * (ammoNumber + 1)) +end; + + +procedure sendAmmoConfig(var ammo: TAmmo); +var i: Longword; +begin + with ammo do + begin + ipcToEngine('eammloadt ' + ammo.a); + ipcToEngine('eammprob ' + ammo.b); + ipcToEngine('eammdelay ' + ammo.c); + ipcToEngine('eammreinf ' + ammo.d); + ipcToEngine('eammstore'); + end +end; + +end. diff -r 24f309d75da8 -r 6adf27e85cae hedgewars/uFLDrawnMap.pas --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hedgewars/uFLDrawnMap.pas Fri Jul 15 22:39:07 2016 +0300 @@ -0,0 +1,58 @@ +unit uFLDrawnMap; +interface +uses SDLh; + +procedure decodeDrawnMap(data: ansistring; dataSize: Longword; var mapData: PByteArray; var size: Longword); + +implementation +uses uUtils, zlib; + +procedure decodeDrawnMap(data: ansistring; dataSize: Longword; var mapData: PByteArray; var size: Longword); +var i, cl: Longword; + ul: uLong; + s: shortstring; + r: LongInt; + compressedBuf, uncompressedData: PByteArray; +begin + if dataSize = 0 then + begin + mapData:= nil; + size:= 0; + exit; + end; + + compressedBuf:= GetMem(dataSize * 3 div 4); + cl:= 0; + i:= 1; + + while i < dataSize do + begin + if dataSize - i > 240 then + s:= copy(data, i, 240) + else + s:= copy(data, i, dataSize - i + 1); + + s:= DecodeBase64(s); + Move(s[1], compressedBuf^[cl], byte(s[0])); + inc(i, 240); + inc(cl, byte(s[0])); + end; + + ul:= SDLNet_Read32(compressedBuf); + uncompressedData:= GetMem(ul); + r:= uncompress(pBytef(uncompressedData), @ul, @(compressedBuf^[4]), cl - 4); + FreeMem(compressedBuf, dataSize * 3 div 4); + + if r = Z_OK then + begin + mapData:= uncompressedData; + size:= ul + end else + begin + FreeMem(uncompressedData, ul); + mapData:= nil; + size:= 0 + end; +end; + +end. diff -r 24f309d75da8 -r 6adf27e85cae hedgewars/uFLGameConfig.pas --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hedgewars/uFLGameConfig.pas Fri Jul 15 22:39:07 2016 +0300 @@ -0,0 +1,688 @@ +unit uFLGameConfig; +interface +uses uFLTypes; + +procedure resetGameConfig; cdecl; +procedure runQuickGame; cdecl; +procedure runLocalGame; cdecl; +procedure getPreview; cdecl; + +procedure setSeed(seed: PChar); cdecl; +function getSeed: PChar; cdecl; +procedure setTheme(themeName: PChar); cdecl; +procedure setScript(scriptName: PChar); cdecl; +procedure setScheme(schemeName: PChar); cdecl; +procedure setAmmo(ammoName: PChar); cdecl; + +procedure tryAddTeam(teamName: PChar); cdecl; +procedure tryRemoveTeam(teamName: PChar); cdecl; +procedure changeTeamColor(teamName: PChar; dir: LongInt); cdecl; + +procedure netSetSeed(seed: shortstring); +procedure netSetTheme(themeName: shortstring); +procedure netSetScript(scriptName: shortstring); +procedure netSetFeatureSize(fsize: LongInt); +procedure netSetMapGen(mapgen: LongInt); +procedure netSetMap(map: shortstring); +procedure netSetMazeSize(mazesize: LongInt); +procedure netSetTemplate(template: LongInt); +procedure netSetAmmo(name: shortstring; definition: ansistring); +procedure netSetScheme(scheme: TScheme); +procedure netAddTeam(team: TTeam); +procedure netAcceptedTeam(teamName: shortstring); +procedure netSetTeamColor(team: shortstring; color: Longword); +procedure netSetHedgehogsNumber(team: shortstring; hogsNumber: Longword); +procedure netRemoveTeam(teamName: shortstring); +procedure netDrawnData(data: ansistring); +procedure netResetTeams(); +procedure updatePreviewIfNeeded; + +procedure sendConfig(config: PGameConfig); +procedure runNetGame(); + +implementation +uses uFLIPC, uFLUtils, uFLTeams, uFLThemes, uFLSChemes, uFLAmmo + , uFLUICallback, uFLRunQueue, uFLNet, uUtils, uFLDrawnMap + , SDLh; + +var + currentConfig: TGameConfig; + previewNeedsUpdate: boolean; + +function getScriptPath(scriptName: shortstring): shortstring; +begin + getScriptPath:= '/Scripts/Multiplayer/' + scriptName + '.lua' +end; + +procedure sendDrawnMap(config: PGameConfig); +var i: Longword; + data: PByteArray; + dataLen: Longword; + s: shortstring; +begin + decodeDrawnMap(config^.drawnData, config^.drawnDataSize, data, dataLen); + + i:= 0; + + s[0]:= #240; + while i < dataLen do + begin + if dataLen - i > 240 then + begin + Move(data^[i], s[1], 240) + end else + begin + Move(data^[i], s[1], dataLen - i); + s[0]:= char(dataLen - i) + end; + + ipcToEngine('edraw ' + s); + inc(i, 240) + end; + + if dataLen > 0 then + FreeMem(data, dataLen); +end; + +procedure sendConfig(config: PGameConfig); +var i: Longword; +begin +with config^ do +begin + case gameType of + gtPreview: begin + if script <> 'Normal' then + ipcToEngine('escript ' + getScriptPath(script)); + ipcToEngine('eseed ' + seed); + ipcToEngine('e$mapgen ' + intToStr(mapgen)); + if (mapgen = 1) or (mapgen = 2) then + ipcToEngine('e$maze_size ' + intToStr(mazeSize)) + else + ipcToEngine('e$template_filter ' + intToStr(template)); + ipcToEngine('e$feature_size ' + intToStr(featureSize)); + if mapgen = 3 then + sendDrawnMap(config); + end; +gtLocal, gtNet: begin + if gameType = gtNet then + ipcToEngine('TN'); + if script <> 'Normal' then + ipcToEngine('escript ' + getScriptPath(script)); + ipcToEngine('eseed ' + seed); + ipcToEngine('e$mapgen ' + intToStr(mapgen)); + if (mapgen = 1) or (mapgen = 2) then + ipcToEngine('e$maze_size ' + intToStr(mazeSize)) + else + ipcToEngine('e$template_filter ' + intToStr(template)); + ipcToEngine('e$feature_size ' + intToStr(featureSize)); + ipcToEngine('e$theme ' + theme); + if mapgen = 3 then + sendDrawnMap(config); + + sendSchemeConfig(scheme); + + i:= 0; + while (i < 8) and (teams[i].hogsNumber > 0) do + begin + sendTeamConfig(config^.scheme.health, teams[i]); + sendAmmoConfig(config^.ammo); + inc(i) + end; + end; + end; + + ipcToEngine('!'); +end; +end; + +procedure resetGameConfig; cdecl; +var i: Longword; +begin + with currentConfig do + begin + script:= 'Normal'; + + for i:= 0 to 7 do + teams[i].hogsNumber:= 0 + end +end; + +procedure setSeed(seed: PChar); cdecl; +begin + sendUI(mtSeed, @seed[1], length(seed)); + currentConfig.seed:= seed +end; + +function getSeed: PChar; cdecl; +begin + getSeed:= str2PChar(currentConfig.seed) +end; + +function getUnusedColor: Longword; +var i, c: Longword; + fColorMatched: boolean; +begin + c:= 0; + i:= 0; + repeat + repeat + fColorMatched:= (currentConfig.teams[i].hogsNumber > 0) and (currentConfig.teams[i].color = c); + inc(i) + until (i >= 8) or (currentConfig.teams[i].hogsNumber = 0) or fColorMatched; + + if fColorMatched then + begin + i:= 0; + inc(c) + end; + until not fColorMatched; + + getUnusedColor:= c +end; + +procedure runQuickGame; cdecl; +begin + with currentConfig do + begin + gameType:= gtLocal; + arguments[0]:= ''; + arguments[1]:= '--internal'; + arguments[2]:= '--nomusic'; + argumentsNumber:= 3; + + teams[0]:= createRandomTeam; + teams[0].color:= 0; + teams[1]:= createRandomTeam; + teams[1].color:= 1; + teams[1].botLevel:= 3; + + queueExecution(currentConfig); + end; +end; + + +procedure getPreview; cdecl; +begin + previewNeedsUpdate:= false; + + with currentConfig do + begin + gameType:= gtPreview; + arguments[0]:= ''; + arguments[1]:= '--internal'; + arguments[2]:= '--landpreview'; + argumentsNumber:= 3; + + queueExecution(currentConfig); + end; +end; + +procedure runLocalGame; cdecl; +begin + with currentConfig do + begin + gameType:= gtLocal; + arguments[0]:= ''; + arguments[1]:= '--internal'; + arguments[2]:= '--nomusic'; + argumentsNumber:= 3; + + queueExecution(currentConfig); + end; +end; + +procedure runNetGame(); +begin + with currentConfig do + begin + gameType:= gtNet; + arguments[0]:= ''; + arguments[1]:= '--internal'; + arguments[2]:= '--nomusic'; + argumentsNumber:= 3; + + queueExecution(currentConfig); + end; +end; + +procedure tryAddTeam(teamName: PChar); cdecl; +var msg: ansistring; + i, hn, hedgehogsNumber: Longword; + team: PTeam; + c: Longword; +begin + team:= teamByName(teamName); + if team = nil then exit; + + if isConnected then + sendTeam(team^) + else + with currentConfig do + begin + hedgehogsNumber:= 0; + i:= 0; + + while (i < 8) and (teams[i].hogsNumber > 0) do + begin + inc(i); + inc(hedgehogsNumber, teams[i].hogsNumber) + end; + + // no free space for a team or reached hogs number maximum + if (i > 7) or (hedgehogsNumber >= 48) then exit; + + c:= getUnusedColor; + + teams[i]:= team^; + teams[i].extDriven:= false; + + if i = 0 then hn:= 4 else hn:= teams[i - 1].hogsNumber; + if hn > 48 - hedgehogsNumber then hn:= 48 - hedgehogsNumber; + teams[i].hogsNumber:= hn; + + teams[i].color:= c; + + msg:= '0' + #10 + teamName; + sendUI(mtAddPlayingTeam, @msg[1], length(msg)); + + msg:= teamName + #10 + colorsSet[teams[i].color]; + sendUI(mtTeamColor, @msg[1], length(msg)); + + msg:= teamName + #10 + IntToStr(hn); + sendUI(mtHedgehogsNumber, @msg[1], length(msg)); + + msg:= teamName; + sendUI(mtRemoveTeam, @msg[1], length(msg)) + end +end; + + +procedure tryRemoveTeam(teamName: PChar); cdecl; +var i: Longword; + tn: shortstring; + isLocal: boolean; +begin + with currentConfig do + begin + i:= 0; + tn:= teamName; + while (i < 8) and (teams[i].teamName <> tn) do + inc(i); + + // team not found??? + if (i > 7) then exit; + + isLocal:= not teams[i].extDriven; + + if isConnected and not isLocal then + exit; // we cannot remove this team + + while (i < 7) and (teams[i + 1].hogsNumber > 0) do + begin + teams[i]:= teams[i + 1]; + inc(i) + end; + + teams[i].hogsNumber:= 0 + end; + + sendUI(mtRemovePlayingTeam, @tn[1], length(tn)); + if isConnected then + removeTeam(tn); + if isLocal then + sendUI(mtAddTeam, @tn[1], length(tn)) +end; + + +procedure changeTeamColor(teamName: PChar; dir: LongInt); cdecl; +var i, dc: Longword; + tn: shortstring; + msg: ansistring; +begin + with currentConfig do + begin + i:= 0; + tn:= teamName; + while (i < 8) and (teams[i].teamName <> tn) do + inc(i); + // team not found??? + if (i > 7) then exit; + + if dir >= 0 then dc:= 1 else dc:= 8; + teams[i].color:= (teams[i].color + dc) mod 9; + + msg:= tn + #10 + colorsSet[teams[i].color]; + sendUI(mtTeamColor, @msg[1], length(msg)) + end +end; + +procedure setTheme(themeName: PChar); cdecl; +begin + currentConfig.theme:= themeName +end; + +procedure setScript(scriptName: PChar); cdecl; +begin + currentConfig.script:= scriptName +end; + +procedure setScheme(schemeName: PChar); cdecl; +var scheme: PScheme; +begin + scheme:= schemeByName(schemeName); + + if scheme <> nil then + currentConfig.scheme:= scheme^ +end; + +procedure setAmmo(ammoName: PChar); cdecl; +var ammo: PAmmo; +begin + ammo:= ammoByName(ammoName); + + if ammo <> nil then + currentConfig.ammo:= ammo^ +end; + +procedure netSetSeed(seed: shortstring); +begin + if seed <> currentConfig.seed then + begin + currentConfig.seed:= seed; + sendUI(mtSeed, @seed[1], length(seed)); + + getPreview() + end +end; + +procedure netSetTheme(themeName: shortstring); +begin + if themeName <> currentConfig.theme then + begin + currentConfig.theme:= themeName; + sendUI(mtTheme, @themeName[1], length(themeName)) + end +end; + +procedure netSetScript(scriptName: shortstring); +begin + if scriptName <> currentConfig.script then + begin + previewNeedsUpdate:= true; + currentConfig.script:= scriptName; + sendUI(mtScript, @scriptName[1], length(scriptName)) + end +end; + +procedure netSetFeatureSize(fsize: LongInt); +var s: shortstring; +begin + if fsize <> currentConfig.featureSize then + begin + previewNeedsUpdate:= true; + currentConfig.featureSize:= fsize; + s:= IntToStr(fsize); + sendUI(mtFeatureSize, @s[1], length(s)) + end +end; + +procedure netSetMapGen(mapgen: LongInt); +var s: shortstring; +begin + if mapgen <> currentConfig.mapgen then + begin + previewNeedsUpdate:= true; + currentConfig.mapgen:= mapgen; + s:= IntToStr(mapgen); + sendUI(mtMapGen, @s[1], length(s)) + end +end; + +procedure netSetMap(map: shortstring); +begin + sendUI(mtMap, @map[1], length(map)) +end; + +procedure netSetMazeSize(mazesize: LongInt); +var s: shortstring; +begin + if mazesize <> currentConfig.mazesize then + begin + previewNeedsUpdate:= true; + currentConfig.mazesize:= mazesize; + s:= IntToStr(mazesize); + sendUI(mtMazeSize, @s[1], length(s)) + end +end; + +procedure netSetTemplate(template: LongInt); +var s: shortstring; +begin + if template <> currentConfig.template then + begin + previewNeedsUpdate:= true; + currentConfig.template:= template; + s:= IntToStr(template); + sendUI(mtTemplate, @s[1], length(s)) + end +end; + +procedure updatePreviewIfNeeded; +begin + if previewNeedsUpdate then + getPreview +end; + +procedure netSetAmmo(name: shortstring; definition: ansistring); +var ammo: TAmmo; + i: LongInt; +begin + ammo.ammoName:= name; + i:= length(definition) div 4; + ammo.a:= copy(definition, 1, i); + ammo.b:= copy(definition, i + 1, i); + ammo.c:= copy(definition, i * 2 + 1, i); + ammo.d:= copy(definition, i * 3 + 1, i); + + currentConfig.ammo:= ammo; + sendUI(mtAmmo, @name[1], length(name)) +end; + +procedure netSetScheme(scheme: TScheme); +begin + currentConfig.scheme:= scheme; + sendUI(mtScheme, @scheme.schemeName[1], length(scheme.schemeName)) +end; + +procedure netAddTeam(team: TTeam); +var msg: ansistring; + i, hn, hedgehogsNumber: Longword; + c: Longword; +begin + with currentConfig do + begin + hedgehogsNumber:= 0; + i:= 0; + + while (i < 8) and (teams[i].hogsNumber > 0) do + begin + inc(i); + inc(hedgehogsNumber, teams[i].hogsNumber) + end; + + // no free space for a team - server bug??? + if (i > 7) or (hedgehogsNumber >= 48) then exit; + + c:= getUnusedColor; + + teams[i]:= team; + teams[i].extDriven:= true; + + if i = 0 then hn:= 4 else hn:= teams[i - 1].hogsNumber; + if hn > 48 - hedgehogsNumber then hn:= 48 - hedgehogsNumber; + teams[i].hogsNumber:= hn; + + teams[i].color:= c; + + msg:= '0' + #10 + team.teamName; + sendUI(mtAddPlayingTeam, @msg[1], length(msg)); + + msg:= team.teamName + #10 + colorsSet[teams[i].color]; + sendUI(mtTeamColor, @msg[1], length(msg)); + end +end; + +procedure netAcceptedTeam(teamName: shortstring); +var msg: ansistring; + i, hn, hedgehogsNumber: Longword; + c: Longword; + team: PTeam; +begin + with currentConfig do + begin + team:= teamByName(teamName); + // no such team??? + if team = nil then exit; + + hedgehogsNumber:= 0; + i:= 0; + + while (i < 8) and (teams[i].hogsNumber > 0) do + begin + inc(i); + inc(hedgehogsNumber, teams[i].hogsNumber) + end; + + // no free space for a team - server bug??? + if (i > 7) or (hedgehogsNumber >= 48) then exit; + + c:= getUnusedColor; + + teams[i]:= team^; + teams[i].extDriven:= false; + + if i = 0 then hn:= 4 else hn:= teams[i - 1].hogsNumber; + if hn > 48 - hedgehogsNumber then hn:= 48 - hedgehogsNumber; + teams[i].hogsNumber:= hn; + + teams[i].color:= c; + + msg:= '0' + #10 + teamName; + sendUI(mtAddPlayingTeam, @msg[1], length(msg)); + + msg:= teamName + #10 + colorsSet[teams[i].color]; + sendUI(mtTeamColor, @msg[1], length(msg)); + + msg:= teamName; + sendUI(mtRemoveTeam, @msg[1], length(msg)) + end +end; + +procedure netRemoveTeam(teamName: shortstring); +var msg: shortstring; + i: Longword; + tn: shortstring; + isLocal: boolean; +begin + with currentConfig do + begin + i:= 0; + tn:= teamName; + while (i < 8) and (teams[i].teamName <> tn) do + inc(i); + + // team not found??? + if (i > 7) then exit; + + isLocal:= not teams[i].extDriven; + + while (i < 7) and (teams[i + 1].hogsNumber > 0) do + begin + teams[i]:= teams[i + 1]; + inc(i) + end; + + teams[i].hogsNumber:= 0 + end; + + msg:= teamName; + + sendUI(mtRemovePlayingTeam, @msg[1], length(msg)); + if isLocal then + sendUI(mtAddTeam, @msg[1], length(msg)) +end; + +procedure netSetTeamColor(team: shortstring; color: Longword); +var i: Longword; + msg: ansistring; +begin + with currentConfig do + begin + i:= 0; + + while (i < 8) and (teams[i].teamName <> team) do + inc(i); + // team not found??? + if (i > 7) then exit; + + teams[i].color:= color mod 9; + + msg:= team + #10 + colorsSet[teams[i].color]; + sendUI(mtTeamColor, @msg[1], length(msg)) + end +end; + +procedure netSetHedgehogsNumber(team: shortstring; hogsNumber: Longword); +var i: Longword; + msg: ansistring; +begin + if hogsNumber > 8 then exit; + + with currentConfig do + begin + i:= 0; + + while (i < 8) and (teams[i].teamName <> team) do + inc(i); + // team not found??? + if (i > 7) then exit; + + teams[i].hogsNumber:= hogsNumber; + + msg:= team + #10 + IntToStr(hogsNumber); + sendUI(mtHedgehogsNumber, @msg[1], length(msg)) + end +end; + +procedure netResetTeams(); +var msg: shortstring; + i: Longword; +begin + with currentConfig do + begin + i:= 0; + + while (i < 8) and (teams[i].hogsNumber > 0) do + begin + msg:= teams[i].teamName; + + sendUI(mtRemovePlayingTeam, @msg[1], length(msg)); + if not teams[i].extDriven then + sendUI(mtAddTeam, @msg[1], length(msg)); + + teams[i].hogsNumber:= 0; + inc(i) + end; + + end; +end; + +procedure netDrawnData(data: ansistring); +begin + currentConfig.drawnDataSize:= length(data); + currentConfig.drawnData:= data; + + getPreview +end; + +end. diff -r 24f309d75da8 -r 6adf27e85cae hedgewars/uFLIPC.pas --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hedgewars/uFLIPC.pas Fri Jul 15 22:39:07 2016 +0300 @@ -0,0 +1,326 @@ +unit uFLIPC; +interface +uses SDLh, uFLTypes; + +procedure initIPC; +procedure freeIPC; + +procedure ipcToEngine(s: shortstring); +procedure ipcToEngineRaw(p: pointer; len: Longword); +procedure ipcSetEngineBarrier(); +procedure ipcRemoveBarrierFromEngineQueue(); +//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: TIPCMessage; +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; + queueFrontend, queueEngine, queueNet: PIPCQueue; + +procedure ipcSend(var s: TIPCMessage; queue: PIPCQueue); +var pmsg: PIPCMessage; +begin + SDL_LockMutex(queue^.mut); + + s.next:= nil; + s.barrier:= 0; + + if (queue^.msg.next = nil) and (queue^.msg.str[0] = #0) and (queue^.msg.buf = nil) and (queue^.msg.barrier = 0) then + begin + queue^.msg:= s; + end else + begin + new(pmsg); + pmsg^:= s; + queue^.last^.next:= pmsg; + queue^.last:= pmsg; + end; + + SDL_CondSignal(queue^.cond); + SDL_UnlockMutex(queue^.mut); +end; + +function ipcRead(queue: PIPCQueue): TIPCMessage; +var pmsg: PIPCMessage; +begin + SDL_LockMutex(queue^.mut); + while ((queue^.msg.str[0] = #0) and (queue^.msg.buf = nil)) + and ((queue^.msg.barrier > 0) or (queue^.msg.next = nil) or ((queue^.msg.next^.barrier > 0) and (queue^.msg.next^.str[0] = #0) and (queue^.msg.next^.buf = nil))) do + SDL_CondWait(queue^.cond, queue^.mut); + + if (queue^.msg.str[0] <> #0) or (queue^.msg.buf <> nil) then + begin + ipcRead:= queue^.msg; + queue^.msg.str[0]:= #0; + queue^.msg.buf:= nil; + end else + begin + pmsg:= queue^.msg.next; + ipcRead:= pmsg^; + if pmsg^.barrier > 0 then + begin + pmsg^.str[0]:= #0; + pmsg^.buf:= nil + end else + begin + queue^.msg.next:= pmsg^.next; + if queue^.msg.next = nil then queue^.last:= @queue^.msg; + dispose(pmsg) + end + end; + + SDL_UnlockMutex(queue^.mut) +end; + +function ipcCheck(queue: PIPCQueue): boolean; +begin + SDL_LockMutex(queue^.mut); + ipcCheck:= (queue^.msg.str[0] > #0) or (queue^.msg.buf <> nil) or + ((queue^.msg.barrier = 0) and (queue^.msg.next <> nil) and ((queue^.msg.next^.barrier = 0) or (queue^.msg.next^.str[0] <> #0) or (queue^.msg.next^.buf <> nil))); + SDL_UnlockMutex(queue^.mut) +end; + +procedure ipcToEngine(s: shortstring); +var msg: TIPCMessage; +begin + msg.str:= s; + msg.buf:= nil; + ipcSend(msg, queueEngine) +end; + +procedure ipcToFrontend(s: shortstring); +var msg: TIPCMessage; +begin + msg.str:= s; + msg.buf:= nil; + ipcSend(msg, queueFrontend) +end; + +procedure ipcSetEngineBarrier(); +begin + SDL_LockMutex(queueEngine^.mut); + + inc(queueEngine^.last^.barrier); + + SDL_UnlockMutex(queueEngine^.mut); +end; + +procedure ipcRemoveBarrierFromEngineQueue(); +var pmsg, t: PIPCMessage; + q: PIPCQueue; +begin + q:= queueEngine; + + SDL_LockMutex(q^.mut); + + pmsg:= @q^.msg; + while pmsg <> nil do + begin + t:= pmsg^.next; + q^.msg.next:= t; + + pmsg^.str[0]:= #0; + if pmsg^.buf <> nil then + begin + FreeMem(pmsg^.buf, pmsg^.len); + pmsg^.buf:= nil + end; + + if pmsg <> @q^.msg then + if pmsg^.barrier = 0 then + dispose(pmsg) + else + if pmsg^.barrier = 1 then + begin + dispose(pmsg); + t:= nil + end else + begin + dec(pmsg^.barrier); + q^.msg.next:= pmsg; + t:= nil + end + else + if pmsg^.barrier > 0 then + begin + dec(pmsg^.barrier); + t:= nil + end; + + pmsg:= t + end; + + if q^.msg.next = nil then q^.last:= @q^.msg; + + q^.msg.str[0]:= #0; + q^.msg.buf:= nil; + + SDL_UnlockMutex(q^.mut); +end; + +procedure ipcToNet(s: shortstring); +var msg: TIPCMessage; +begin + msg.str:= s; + msg.buf:= nil; + ipcSend(msg, queueNet) +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, queueEngine) +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, queueFrontend) +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, queueNet) +end; + +function ipcReadFromEngine: TIPCMessage; +begin + ipcReadFromEngine:= ipcRead(queueFrontend) +end; + +function ipcReadFromFrontend: TIPCMessage; +begin + ipcReadFromFrontend:= ipcRead(queueEngine) +end; + +function ipcReadToNet: TIPCMessage; +begin + ipcReadToNet:= ipcRead(queueNet) +end; + +function ipcCheckFromEngine: boolean; +begin + ipcCheckFromEngine:= ipcCheck(queueFrontend) +end; + +function ipcCheckFromFrontend: boolean; +begin + ipcCheckFromFrontend:= ipcCheck(queueEngine) +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; + +function createQueue: PIPCQueue; +var q: PIPCQueue; +begin + new(q); + q^.msg.str:= ''; + q^.msg.buf:= nil; + q^.msg.barrier:= 0; + q^.mut:= SDL_CreateMutex; + q^.cond:= SDL_CreateCond; + q^.msg.next:= nil; + q^.last:= @q^.msg; + createQueue:= q +end; + +procedure destroyQueue(queue: PIPCQueue); +begin + SDL_DestroyCond(queue^.cond); + SDL_DestroyMutex(queue^.mut); + dispose(queue); +end; + +procedure initIPC; +begin + queueFrontend:= createQueue; + queueEngine:= createQueue; + queueNet:= createQueue; + + callbackPointerF:= nil; + callbackListenerThreadF:= nil; +end; + +procedure freeIPC; +begin + //FIXME SDL_KillThread(callbackListenerThreadF); + //FIXME SDL_KillThread(callbackListenerThreadN); + destroyQueue(queueFrontend); + destroyQueue(queueEngine); + destroyQueue(queueNet); +end; + +end. diff -r 24f309d75da8 -r 6adf27e85cae hedgewars/uFLNet.pas --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hedgewars/uFLNet.pas Fri Jul 15 22:39:07 2016 +0300 @@ -0,0 +1,414 @@ +unit uFLNet; +interface + +procedure connectOfficialServer; + +procedure initModule; +procedure freeModule; +procedure sendNet(s: shortstring); +procedure sendNetLn(s: shortstring); + +var isConnected: boolean = false; + +implementation +uses SDLh, uFLIPC, uFLTypes, uFLUICallback, uFLNetTypes, uFLUtils; + +const endCmd: shortstring = #10 + #10; + +function getNextChar: char; forward; +function getCurrChar: char; forward; + +type + TParserState = record + cmd: TCmdType; + l: LongInt; + buf: shortstring; + bufpos: byte; + end; + PHandler = procedure; + +var state: TParserState; + +procedure handleTail; forward; +function getShortString: shortstring; forward; +function getLongString: ansistring; forward; + +procedure handler_; +begin + sendUI(mtNetData, @state.cmd, sizeof(state.cmd)); + handleTail() +end; + +procedure handler_L; +var cmd: TCmdParamL; + s: ansistring; +begin + cmd.cmd:= state.cmd; + s:= getLongString; + cmd.str1len:= length(s); + if cmd.str1len = 0 then exit; + cmd.str1:= s; + sendUI(mtNetData, @cmd, sizeof(cmd)); + handleTail() +end; + +procedure handler_ML; +var cmd: TCmdParamL; + f: boolean; + s: ansistring; +begin + sendUI(mtNetData, @state.cmd, sizeof(state.cmd)); + cmd.cmd:= Succ(state.cmd); + + repeat + s:= getLongString; + cmd.str1len:= length(s); + f:= cmd.str1len <> 0; + + if f then + begin + cmd.str1:= s; + sendUI(mtNetData, @cmd, sizeof(cmd)); + end + until not f; + state.l:= 0 +end; + +procedure handler_MS; +var cmd: TCmdParamS; + f: boolean; +begin + sendUI(mtNetData, @state.cmd, sizeof(state.cmd)); + cmd.cmd:= Succ(state.cmd); + + repeat + cmd.str1:= getShortString; + f:= cmd.str1[0] <> #0; + if f then + sendUI(mtNetData, @cmd, sizeof(cmd)); + until not f; + state.l:= 0 +end; + +procedure handler_S; +var cmd: TCmdParamS; +begin + cmd.cmd:= state.cmd; + cmd.str1:= getShortString; + if cmd.str1[0] = #0 then exit; + sendUI(mtNetData, @cmd, sizeof(cmd)); + handleTail() +end; + +procedure handler_SL; +var cmd: TCmdParamSL; +begin + cmd.cmd:= state.cmd; + cmd.str1:= getShortString; + if cmd.str1[0] = #0 then exit; + cmd.str2:= getLongString; + if cmd.str2[0] = #0 then exit; + sendUI(mtNetData, @cmd, sizeof(cmd)); + handleTail() +end; + +procedure handler_SMS; +var cmd: TCmdParamS; + f: boolean; +begin + cmd.cmd:= state.cmd; + cmd.str1:= getShortString; + if cmd.str1[0] = #0 then exit; + sendUI(mtNetData, @cmd, sizeof(cmd)); + + cmd.cmd:= Succ(state.cmd); + repeat + cmd.str1:= getShortString; + f:= cmd.str1[0] <> #0; + if f then + sendUI(mtNetData, @cmd, sizeof(cmd)); + until not f; + state.l:= 0 +end; + +procedure handler_SS; +var cmd: TCmdParamSS; +begin + cmd.cmd:= state.cmd; + cmd.str1:= getShortString; + if cmd.str1[0] = #0 then exit; + cmd.str2:= getShortString; + if cmd.str2[0] = #0 then exit; + sendUI(mtNetData, @cmd, sizeof(cmd)); + handleTail() +end; + +procedure handler__i; +var cmd: TCmdParami; + s: shortstring; +begin + s:= getShortString(); + if s[0] = #0 then exit; + cmd.cmd:= state.cmd; + s:= getShortString(); + if s[0] = #0 then exit; + cmd.param1:= strToInt(s); + sendUI(mtNetData, @cmd, sizeof(cmd)); + handleTail() +end; + +procedure handler_i; +var cmd: TCmdParami; + s: shortstring; +begin + s:= getShortString(); + if s[0] = #0 then exit; + cmd.cmd:= state.cmd; + cmd.param1:= strToInt(s); + sendUI(mtNetData, @cmd, sizeof(cmd)); + handleTail() +end; + +procedure handler__UNKNOWN_; +begin + //writeln('[NET] Unknown cmd'); + handleTail(); + state.l:= 0 +end; + +const net2cmd: array[0..46] of TCmdType = (cmd_WARNING, cmd_WARNING, + cmd_TEAM_COLOR, cmd_TEAM_ACCEPTED, cmd_SERVER_VARS, cmd_SERVER_MESSAGE, + cmd_SERVER_AUTH, cmd_RUN_GAME, cmd_ROUND_FINISHED, cmd_ROOM_UPD, cmd_ROOM_DEL, + cmd_ROOM_ADD, cmd_ROOMS, cmd_REMOVE_TEAM, cmd_PROTO, cmd_PING, cmd_NOTICE, + cmd_NICK, cmd_LOBBY_LEFT, cmd_LOBBY_JOINED, cmd_LEFT, cmd_KICKED, cmd_JOINING, + cmd_JOINED, cmd_INFO, cmd_HH_NUM, cmd_ERROR, cmd_EM, cmd_CONNECTED, + cmd_CLIENT_FLAGS, cmd_CHAT, cmd_CFG_THEME, cmd_CFG_TEMPLATE, cmd_CFG_SEED, + cmd_CFG_SCRIPT, cmd_CFG_SCHEME, cmd_CFG_MAZE_SIZE, cmd_CFG_MAP, cmd_CFG_MAPGEN, + cmd_CFG_FULLMAPCONFIG, cmd_CFG_FEATURE_SIZE, cmd_CFG_DRAWNMAP, cmd_CFG_AMMO, + cmd_BYE, cmd_BANLIST, cmd_ASKPASSWORD, cmd_ADD_TEAM); +const letters: array[0..332] of char = ('A', 'D', 'D', '_', 'T', 'E', 'A', 'M', + #10, 'S', 'K', 'P', 'A', 'S', 'S', 'W', 'O', 'R', 'D', #10, 'B', 'A', 'N', 'L', + 'I', 'S', 'T', #10, 'Y', 'E', #10, 'C', 'F', 'G', #10, 'A', 'M', 'M', 'O', #10, + 'D', 'R', 'A', 'W', 'N', 'M', 'A', 'P', #10, 'F', 'E', 'A', 'T', 'U', 'R', 'E', + '_', 'S', 'I', 'Z', 'E', #10, 'U', 'L', 'L', 'M', 'A', 'P', 'C', 'O', 'N', 'F', + 'I', 'G', #10, 'M', 'A', 'P', 'G', 'E', 'N', #10, #10, 'Z', 'E', '_', 'S', 'I', + 'Z', 'E', #10, 'S', 'C', 'H', 'E', 'M', 'E', #10, 'R', 'I', 'P', 'T', #10, 'E', + 'E', 'D', #10, 'T', 'E', 'M', 'P', 'L', 'A', 'T', 'E', #10, 'H', 'E', 'M', 'E', + #10, 'H', 'A', 'T', #10, 'L', 'I', 'E', 'N', 'T', '_', 'F', 'L', 'A', 'G', 'S', + #10, 'O', 'N', 'N', 'E', 'C', 'T', 'E', 'D', #10, 'E', 'M', #10, 'R', 'R', 'O', + 'R', #10, 'H', 'H', '_', 'N', 'U', 'M', #10, 'I', 'N', 'F', 'O', #10, 'J', 'O', + 'I', 'N', 'E', 'D', #10, 'I', 'N', 'G', #10, 'K', 'I', 'C', 'K', 'E', 'D', #10, + 'L', 'E', 'F', 'T', #10, 'O', 'B', 'B', 'Y', ':', 'J', 'O', 'I', 'N', 'E', 'D', + #10, 'L', 'E', 'F', 'T', #10, 'N', 'I', 'C', 'K', #10, 'O', 'T', 'I', 'C', 'E', + #10, 'P', 'I', 'N', 'G', #10, 'R', 'O', 'T', 'O', #10, 'R', 'E', 'M', 'O', 'V', + 'E', '_', 'T', 'E', 'A', 'M', #10, 'O', 'O', 'M', 'S', #10, #10, 'A', 'D', 'D', + #10, 'D', 'E', 'L', #10, 'U', 'P', 'D', #10, 'U', 'N', 'D', '_', 'F', 'I', 'N', + 'I', 'S', 'H', 'E', 'D', #10, 'U', 'N', '_', 'G', 'A', 'M', 'E', #10, 'S', 'E', + 'R', 'V', 'E', 'R', '_', 'A', 'U', 'T', 'H', #10, 'M', 'E', 'S', 'S', 'A', 'G', + 'E', #10, 'V', 'A', 'R', 'S', #10, 'T', 'E', 'A', 'M', '_', 'A', 'C', 'C', 'E', + 'P', 'T', 'E', 'D', #10, 'C', 'O', 'L', 'O', 'R', #10, 'W', 'A', 'R', 'N', 'I', + 'N', 'G', #10, #0, #10); +const commands: array[0..332] of integer = (20, 8, 0, 0, 0, 0, 0, 0, -56, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, -55, 11, 7, 0, 0, 0, 0, 0, -54, 0, 0, -53, 115, 89, 0, + 0, 5, 0, 0, 0, -52, 9, 0, 0, 0, 0, 0, 0, 0, -51, 26, 12, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, -50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -49, 16, 0, 6, 4, 0, 0, -48, -47, + 0, 0, 0, 0, 0, 0, 0, -46, 16, 11, 5, 0, 0, 0, -45, 0, 0, 0, 0, -44, 0, 0, 0, + -43, 0, 8, 0, 0, 0, 0, 0, 0, -42, 0, 0, 0, 0, -41, 4, 0, 0, -40, 12, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, -39, 0, 0, 0, 0, 0, 0, 0, 0, -38, 8, 2, -37, 0, 0, 0, 0, -36, + 7, 0, 0, 0, 0, 0, -35, 5, 0, 0, 0, -34, 11, 0, 0, 0, 3, 0, -33, 0, 0, 0, -32, 7, + 0, 0, 0, 0, 0, -31, 22, 4, 0, 0, -30, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, -29, 0, + 0, 0, 0, -28, 11, 4, 0, 0, -27, 0, 0, 0, 0, 0, -26, 10, 4, 0, 0, -25, 0, 0, 0, + 0, -24, 51, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, -23, 31, 17, 0, 2, -22, 0, 4, 0, 0, + -21, 4, 0, 0, -20, 0, 0, 0, -19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -18, 0, 0, + 0, 0, 0, 0, 0, -17, 25, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, -16, 8, 0, 0, 0, 0, 0, 0, + -15, 0, 0, 0, 0, -14, 20, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, -13, 0, 0, 0, 0, + 0, -12, 8, 0, 0, 0, 0, 0, 0, -11, 0, -10); +const handlers: array[0..46] of PHandler = (@handler__UNKNOWN_, @handler_L, + @handler_SS, @handler_S, @handler_SL, @handler_L, @handler_S, @handler_, + @handler_, @handler_MS, @handler_S, @handler_MS, @handler_MS, @handler_S, + @handler_i, @handler_MS, @handler_L, @handler_S, @handler_SL, @handler_MS, + @handler_SL, @handler_, @handler_S, @handler_MS, @handler_MS, @handler_SS, + @handler_L, @handler_ML, @handler__i, @handler_SMS, @handler_SL, @handler_S, + @handler_i, @handler_S, @handler_S, @handler_MS, @handler_i, @handler_i, + @handler_S, @handler_MS, @handler_i, @handler_L, @handler_SL, @handler_SL, + @handler_MS, @handler_S, @handler_MS); + +procedure handleTail; +var cnt: Longint; + c: char; +begin + c:= getCurrChar; + repeat + if c = #10 then cnt:= 0 else cnt:= 1; + repeat + c:= getNextChar; + inc(cnt) + until (c = #0) or (c = #10); + until (c = #0) or (cnt = 1); + state.l:= 0 +end; + +var sock: PTCPSocket; + netReaderThread: PSDL_Thread; + +function getCurrChar: char; +begin + getCurrChar:= state.buf[state.bufpos] +end; + +function getNextChar: char; +var r: byte; +begin + if state.bufpos < byte(state.buf[0]) then + begin + inc(state.bufpos); + end else + begin + r:= SDLNet_TCP_Recv(sock, @state.buf[1], 255); + if r > 0 then + begin + state.bufpos:= 1; + state.buf[0]:= char(r); + end else + begin + state.bufpos:= 0; + state.buf[0]:= #0; + end + end; + + getNextChar:= state.buf[state.bufpos]; +end; + +function netReader(data: pointer): LongInt; cdecl; export; +var c: char; + ipaddr: TIPAddress; +begin + netReader:= 0; + + if SDLNet_ResolveHost(ipaddr, PChar('netserver.hedgewars.org'), 46631) = 0 then + sock:= SDLNet_TCP_Open(ipaddr); + + repeat + c:= getNextChar; + //writeln('>>>>> ', c, ' [', letters[state.l], '] ', commands[state.l], ' ', state.l); + if c = #0 then + isConnected:= false + else + begin + while (letters[state.l] <> c) and (commands[state.l] > 0) do + inc(state.l, commands[state.l]); + + if c = letters[state.l] then + if commands[state.l] < 0 then + begin + state.cmd:= net2cmd[-10 - commands[state.l]]; + //writeln('[NET] ', state.cmd); + handlers[-10 - commands[state.l]](); + state.l:= 0 + end + else + inc(state.l) + else + begin + handler__UNKNOWN_() + end + end + until not isConnected; + + SDLNet_TCP_Close(sock); + sock:= nil; + + writeln('[NET] netReader: disconnected'); +end; + +procedure sendNet(s: shortstring); +begin + writeln('[NET] Send: ', s); + ipcToNet(s + endCmd); +end; + +procedure sendNetLn(s: shortstring); +begin + writeln('[NET] Send: ', s); + ipcToNet(s + #10); +end; + +function getShortString: shortstring; +var s: shortstring; + c: char; +begin + s[0]:= #0; + + repeat + inc(s[0]); + s[byte(s[0])]:= getNextChar + until (s[0] = #255) or (s[byte(s[0])] = #10) or (s[byte(s[0])] = #0); + + if s[byte(s[0])] = #10 then + dec(s[0]) + else + repeat c:= getNextChar until (c = #0) or (c = #10); + + getShortString:= s +end; + +function getLongString: ansistring; +var s: shortstring; + l: ansistring; + c: char; +begin + l:= ''; + + repeat + s[0]:= #0; + repeat + inc(s[0]); + c:= getNextChar; + s[byte(s[0])]:= c + until (s[0] = #255) or (c = #10) or (c = #0); + + if s[byte(s[0])] = #10 then + dec(s[0]); + + l:= l + s + until (c = #10) or (c = #0); + + getLongString:= l +end; + +procedure netSendCallback(p: pointer; msg: PChar; len: Longword); +begin + // W A R N I N G: totally thread-unsafe due to use of sock variable + SDLNet_TCP_Send(sock, msg, len); +end; + +procedure connectOfficialServer; +begin + if sock <> nil then + exit; + + state.bufpos:= 0; + state.buf:= ''; + + state.l:= 0; + isConnected:= true; + + netReaderThread:= SDL_CreateThread(@netReader, 'netReader', nil); + SDL_DetachThread(netReaderThread) +end; + +procedure initModule; +begin + sock:= nil; + isConnected:= false; + + SDLNet_Init; + + registerNetCallback(nil, @netSendCallback); +end; + +procedure freeModule; +begin +end; + +end. diff -r 24f309d75da8 -r 6adf27e85cae hedgewars/uFLNetProtocol.pas --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hedgewars/uFLNetProtocol.pas Fri Jul 15 22:39:07 2016 +0300 @@ -0,0 +1,599 @@ +unit uFLNetProtocol; +interface + +procedure passNetData(p: pointer); cdecl; + +procedure sendChatLine(msg: PChar); cdecl; +procedure joinRoom(roomName: PChar); cdecl; +procedure partRoom(msg: PChar); cdecl; + +procedure ResetNetState; + +implementation +uses uFLNetTypes, uFLTypes, uFLUICallback, uFLNet, uFLGameConfig, uFLUtils, uFLIPC, uUtils; + +type + PHandler = procedure (var t: TCmdData); + +var isInRoom: boolean; + myNickname: shortstring; + +procedure onRoomLeaving(); +begin + isInRoom:= false; + sendUI(mtMoveToLobby, nil, 0); + netResetTeams +end; + +var teamIndex: LongInt; + tmpTeam: TTeam; + +const teamFields: array[0..22] of PShortstring = ( + @tmpTeam.teamName + , @tmpTeam.grave + , @tmpTeam.fort + , @tmpTeam.voice + , @tmpTeam.flag + , @tmpTeam.owner + , nil + , @tmpTeam.hedgehogs[0].name + , @tmpTeam.hedgehogs[0].hat + , @tmpTeam.hedgehogs[1].name + , @tmpTeam.hedgehogs[1].hat + , @tmpTeam.hedgehogs[2].name + , @tmpTeam.hedgehogs[2].hat + , @tmpTeam.hedgehogs[3].name + , @tmpTeam.hedgehogs[3].hat + , @tmpTeam.hedgehogs[4].name + , @tmpTeam.hedgehogs[4].hat + , @tmpTeam.hedgehogs[5].name + , @tmpTeam.hedgehogs[5].hat + , @tmpTeam.hedgehogs[6].name + , @tmpTeam.hedgehogs[6].hat + , @tmpTeam.hedgehogs[7].name + , @tmpTeam.hedgehogs[7].hat + ); + +procedure handler_ADD_TEAM(var p: TCmdParam); +begin + teamIndex:= 0; + tmpTeam.color:= 0 +end; + +procedure handler_ADD_TEAM_s(var s: TCmdParamS); +begin + if teamIndex = 6 then + tmpTeam.botLevel:= strToInt(s.str1) + else if teamIndex < 23 then + teamFields[teamIndex]^:= s.str1; + + if teamIndex = 22 then + netAddTeam(tmpTeam); + + inc(teamIndex); +end; + +procedure handler_ASKPASSWORD(var p: TCmdParamS); +begin +end; + +procedure handler_BANLIST(var p: TCmdParam); +begin +end; + +procedure handler_BANLIST_s(var s: TCmdParamS); +begin +end; + +procedure handler_BYE(var p: TCmdParamSL); +begin + sendUI(mtDisconnected, @p.str2[1], length(p.str2)); +end; + +procedure handler_CFG_AMMO(var p: TCmdParamSL); +begin + netSetAmmo(p.str1, p.str2) +end; + +procedure handler_CFG_DRAWNMAP(var p: TCmdParamL); +begin + netDrawnData(copy(ansistring(p.str1), 1, p.str1len)) +end; + +procedure handler_CFG_FEATURE_SIZE(var p: TCmdParami); +begin + if isInRoom then + begin + netSetFeatureSize(p.param1); + updatePreviewIfNeeded + end; +end; + +var fmcfgIndex: integer; + +procedure handler_CFG_FULLMAPCONFIG(var p: TCmdParam); +begin + fmcfgIndex:= 0; +end; + +procedure handler_CFG_FULLMAPCONFIG_s(var s: TCmdParamS); +begin + if not isInRoom then exit; + + inc(fmcfgIndex); + case fmcfgIndex of + 1: netSetFeatureSize(strToInt(s.str1)); + 2: if s.str1[0] <> '+' then netSetMap(s.str1); + 3: netSetMapGen(strToInt(s.str1)); + 4: netSetMazeSize(strToInt(s.str1)); + 5: netSetSeed(s.str1); + 6: begin + netSetTemplate(strToInt(s.str1)); + updatePreviewIfNeeded; + end; + end; +end; + +procedure handler_CFG_MAP(var p: TCmdParamS); +begin + if isInRoom then + netSetMap(p.str1); +end; + +procedure handler_CFG_MAPGEN(var p: TCmdParami); +begin + if isInRoom then + begin + netSetMapGen(p.param1); + updatePreviewIfNeeded + end +end; + +procedure handler_CFG_MAZE_SIZE(var p: TCmdParami); +begin + if isInRoom then + begin + netSetMazeSize(p.param1); + updatePreviewIfNeeded + end +end; + +var schemeIndex: LongInt; + tmpScheme: TScheme; + +procedure handler_CFG_SCHEME(var p: TCmdParam); +begin + schemeIndex:= 0 +end; + +const schemeFields: array[0..43] of pointer = ( + @tmpScheme.schemeName // 0 + , @tmpScheme.fortsmode // 1 + , @tmpScheme.divteams // 2 + , @tmpScheme.solidland // 3 + , @tmpScheme.border // 4 + , @tmpScheme.lowgrav // 5 + , @tmpScheme.laser // 6 + , @tmpScheme.invulnerability // 7 + , @tmpScheme.resethealth // 8 + , @tmpScheme.vampiric // 9 + , @tmpScheme.karma // 10 + , @tmpScheme.artillery // 11 + , @tmpScheme.randomorder // 12 + , @tmpScheme.king // 13 + , @tmpScheme.placehog // 14 + , @tmpScheme.sharedammo // 15 + , @tmpScheme.disablegirders // 16 + , @tmpScheme.disablelandobjects // 17 + , @tmpScheme.aisurvival // 18 + , @tmpScheme.infattack // 19 + , @tmpScheme.resetweps // 20 + , @tmpScheme.perhogammo // 21 + , @tmpScheme.disablewind // 22 + , @tmpScheme.morewind // 23 + , @tmpScheme.tagteam // 24 + , @tmpScheme.bottomborder // 25 + , @tmpScheme.damagefactor // 26 + , @tmpScheme.turntime // 27 + , @tmpScheme.health // 28 + , @tmpScheme.suddendeath // 29 + , @tmpScheme.caseprobability // 30 + , @tmpScheme.minestime // 31 + , @tmpScheme.minesnum // 32 + , @tmpScheme.minedudpct // 33 + , @tmpScheme.explosives // 34 + , @tmpScheme.airmines // 35 + , @tmpScheme.healthprobability // 36 + , @tmpScheme.healthcaseamount // 37 + , @tmpScheme.waterrise // 38 + , @tmpScheme.healthdecrease // 39 + , @tmpScheme.ropepct // 40 + , @tmpScheme.getawaytime // 41 + , @tmpScheme.worldedge // 42 + , @tmpScheme.scriptparam // 43 + ); + +procedure handler_CFG_SCHEME_s(var s: TCmdParamS); +begin + if(schemeIndex = 0) then + tmpScheme.schemeName:= s.str1 + else + if(schemeIndex = 43) then + tmpScheme.scriptparam:= copy(s.str1, 2, length(s.str1) - 1) + else + if(schemeIndex < 26) then + PBoolean(schemeFields[schemeIndex])^:= s.str1[1] = 't' + else + if(schemeIndex < 43) then + PLongInt(schemeFields[schemeIndex])^:= strToInt(s.str1); + + if(schemeIndex = 43) then + netSetScheme(tmpScheme); + + inc(schemeIndex); +end; + +procedure handler_CFG_SCRIPT(var p: TCmdParamS); +begin + if isInRoom then + netSetScript(p.str1) +end; + +procedure handler_CFG_SEED(var p: TCmdParamS); +begin + if isInRoom then + netSetSeed(p.str1) +end; + +procedure handler_CFG_TEMPLATE(var p: TCmdParami); +begin + if isInRoom then + begin + netSetTemplate(p.param1); + updatePreviewIfNeeded + end +end; + +procedure handler_CFG_THEME(var p: TCmdParamS); +begin + if isInRoom then + netSetTheme(p.str1) +end; + +procedure handler_CHAT(var p: TCmdParamSL); +var s: string; +begin + s:= p.str1 + #10 + copy(p.str2, 0, p.str2len); + if isInRoom then + sendUI(mtRoomChatLine, @s[1], length(s)) + else + sendUI(mtLobbyChatLine, @s[1], length(s)); +end; + +var flags: array[TClientFlag] of LongInt; + isFlagsLine: boolean; +procedure handler_CLIENT_FLAGS(var p: TCmdParamS); +var f: TClientFlag; +begin + for f:= Low(TClientFlag) to High(TClientFlag) do + flags[f]:= 0; + + isFlagsLine:= true; +end; + +procedure handler_CLIENT_FLAGS_s(var s: TCmdParamS); +var isRemoval: boolean; + flagValue, i: LongInt; +begin + if isFlagsLine then + begin + if s.str1[1] = '+' then flagValue:= 1 else flagValue:= -1; + for i:= 2 to Length(s.str1) do + case s.str1[1] of + 'r': flags[cfReady]:= flagValue; + 'u': flags[cfRegistered]:= flagValue; + 'i': flags[cfInRoom]:= flagValue; + 'c': flags[cfContributor]:= flagValue; + 'g': flags[cfInGame]:= flagValue; + 'h': flags[cfRoomAdmin]:= flagValue; + 'a': flags[cfServerAdmin]:= flagValue; + end; + + isFlagsLine:= false; + end else + begin + + end +end; + +procedure handler_CONNECTED(var p: TCmdParami); +begin + sendUI(mtConnected, nil, 0); + //writeln('Server features version ', p.param1); + sendNet('PROTO' + #10 + '51'); + sendNet('NICK' + #10 + 'qmlfrontend'); +end; + +procedure handler_EM(var p: TCmdParam); +begin +end; + +procedure handler_EM_s(var p: TCmdParamL); +var i, l: Longword; + s: shortstring; +begin + i:= 1; + l:= length(p.str1); + + while i < l do + begin + s:= DecodeBase64(copy(p.str1, i, 240)); + ipcToEngineRaw(@s[1], byte(s[0])); + inc(i, 160) + end; +end; + +procedure handler_ERROR(var p: TCmdParamL); +begin + sendUI(mtError, @p.str1[1], length(p.str1)); +end; + +procedure handler_HH_NUM(var p: TCmdParamSS); +begin + netSetHedgehogsNumber(p.str1, StrToInt(p.str2)) +end; + +procedure handler_INFO(var p: TCmdParam); +begin +end; + +procedure handler_INFO_s(var s: TCmdParamS); +begin +end; + +procedure handler_JOINED(var p: TCmdParam); +begin +end; + +procedure handler_JOINED_s(var s: TCmdParamS); +begin + if s.str1 = myNickname then // we joined a room + begin + isInRoom:= true; + sendUI(mtMoveToRoom, nil, 0); + end; + + sendUI(mtAddRoomClient, @s.str1[1], length(s.str1)); +end; + +procedure handler_JOINING(var p: TCmdParamS); +begin +end; + +procedure handler_KICKED(var p: TCmdParam); +begin + onRoomLeaving() +end; + +procedure handler_LEFT(var p: TCmdParamSL); +var s: string; +begin + s:= p.str1 + #10 + copy(p.str2, 0, p.str2len); + sendUI(mtRemoveRoomClient, @s[1], length(s)); +end; + +procedure handler_LOBBY_JOINED(var p: TCmdParam); +begin +end; + +procedure handler_LOBBY_JOINED_s(var s: TCmdParamS); +begin + if s.str1 = myNickname then + begin + sendUI(mtMoveToLobby, nil, 0); + sendNet('LIST'); + end; + + sendUI(mtAddLobbyClient, @s.str1[1], length(s.str1)); +end; + +procedure handler_LOBBY_LEFT(var p: TCmdParamSL); +var s: string; +begin + s:= p.str1 + #10 + copy(p.str2, 0, p.str2len); + sendUI(mtRemoveLobbyClient, @s[1], length(s)); +end; + +procedure handler_NICK(var p: TCmdParamS); +begin + myNickname:= p.str1; + sendUI(mtNickname, @p.str1[1], length(p.str1)); +end; + +procedure handler_NOTICE(var p: TCmdParamL); +begin +end; + +procedure handler_PING(var p: TCmdParam); +begin + sendNet('PONG') +end; + +procedure handler_PING_s(var s: TCmdParamS); +begin +end; + +procedure handler_PROTO(var p: TCmdParami); +begin + //writeln('Protocol ', p.param1) +end; + +procedure handler_REMOVE_TEAM(var p: TCmdParamS); +begin + netRemoveTeam(p.str1) +end; + +var roomInfo: string; + roomLinesCount: integer; + +procedure handler_ROOMS(var p: TCmdParam); +begin + roomInfo:= ''; + roomLinesCount:= 0 +end; + +procedure handler_ROOMS_s(var s: TCmdParamS); +begin + roomInfo:= roomInfo + s.str1 + #10; + + if roomLinesCount = 8 then + begin + sendUI(mtAddRoom, @roomInfo[1], length(roomInfo) - 1); + roomLinesCount:= 0; + roomInfo:= '' + end else inc(roomLinesCount); +end; + +procedure handler_ROOM_ADD(var p: TCmdParam); +begin + roomInfo:= ''; + roomLinesCount:= 0 +end; + +procedure handler_ROOM_ADD_s(var s: TCmdParamS); +begin + roomInfo:= roomInfo + s.str1 + #10; + inc(roomLinesCount); + + if roomLinesCount = 9 then + begin + sendUI(mtAddRoom, @roomInfo[1], length(roomInfo) - 1); + roomInfo:= ''; + roomLinesCount:= 0 + end; +end; + +procedure handler_ROOM_DEL(var p: TCmdParamS); +begin + sendUI(mtRemoveRoom, @p.str1[1], length(p.str1)); +end; + +procedure handler_ROOM_UPD(var p: TCmdParam); +begin + roomInfo:= ''; + roomLinesCount:= 0 +end; + +procedure handler_ROOM_UPD_s(var s: TCmdParamS); +begin + roomInfo:= roomInfo + s.str1 + #10; + inc(roomLinesCount); + + if roomLinesCount = 10 then + sendUI(mtUpdateRoom, @roomInfo[1], length(roomInfo) - 1); +end; + +procedure handler_ROUND_FINISHED(var p: TCmdParam); +begin +end; + +procedure handler_RUN_GAME(var p: TCmdParam); +begin + runNetGame +end; + +procedure handler_SERVER_AUTH(var p: TCmdParamS); +begin +end; + +procedure handler_SERVER_MESSAGE(var p: TCmdParamL); +begin +end; + +procedure handler_SERVER_VARS(var p: TCmdParamSL); +begin +end; + +procedure handler_TEAM_ACCEPTED(var p: TCmdParamS); +begin + netAcceptedTeam(p.str1) +end; + +procedure handler_TEAM_COLOR(var p: TCmdParamSS); +begin + netSetTeamColor(p.str1, StrToInt(p.str2)); +end; + +procedure handler_WARNING(var p: TCmdParamL); +begin + sendUI(mtWarning, @p.str1[1], length(p.str1)); +end; + +const handlers: array[TCmdType] of PHandler = (PHandler(@handler_ADD_TEAM), + PHandler(@handler_ADD_TEAM_s), PHandler(@handler_ASKPASSWORD), + PHandler(@handler_BANLIST), PHandler(@handler_BANLIST_s), + PHandler(@handler_BYE), PHandler(@handler_CFG_AMMO), + PHandler(@handler_CFG_DRAWNMAP), PHandler(@handler_CFG_FEATURE_SIZE), + PHandler(@handler_CFG_FULLMAPCONFIG), PHandler(@handler_CFG_FULLMAPCONFIG_s), + PHandler(@handler_CFG_MAP), PHandler(@handler_CFG_MAPGEN), + PHandler(@handler_CFG_MAZE_SIZE), PHandler(@handler_CFG_SCHEME), + PHandler(@handler_CFG_SCHEME_s), PHandler(@handler_CFG_SCRIPT), + PHandler(@handler_CFG_SEED), PHandler(@handler_CFG_TEMPLATE), + PHandler(@handler_CFG_THEME), PHandler(@handler_CHAT), + PHandler(@handler_CLIENT_FLAGS), PHandler(@handler_CLIENT_FLAGS_s), + PHandler(@handler_CONNECTED), PHandler(@handler_EM), PHandler(@handler_EM_s), + PHandler(@handler_ERROR), PHandler(@handler_HH_NUM), PHandler(@handler_INFO), + PHandler(@handler_INFO_s), PHandler(@handler_JOINED), + PHandler(@handler_JOINED_s), PHandler(@handler_JOINING), + PHandler(@handler_KICKED), PHandler(@handler_LEFT), + PHandler(@handler_LOBBY_JOINED), PHandler(@handler_LOBBY_JOINED_s), + PHandler(@handler_LOBBY_LEFT), PHandler(@handler_NICK), + PHandler(@handler_NOTICE), PHandler(@handler_PING), PHandler(@handler_PING_s), + PHandler(@handler_PROTO), PHandler(@handler_REMOVE_TEAM), + PHandler(@handler_ROOMS), PHandler(@handler_ROOMS_s), + PHandler(@handler_ROOM_ADD), PHandler(@handler_ROOM_ADD_s), + PHandler(@handler_ROOM_DEL), PHandler(@handler_ROOM_UPD), + PHandler(@handler_ROOM_UPD_s), PHandler(@handler_ROUND_FINISHED), + PHandler(@handler_RUN_GAME), PHandler(@handler_SERVER_AUTH), + PHandler(@handler_SERVER_MESSAGE), PHandler(@handler_SERVER_VARS), + PHandler(@handler_TEAM_ACCEPTED), PHandler(@handler_TEAM_COLOR), + PHandler(@handler_WARNING)); + +procedure passNetData(p: pointer); cdecl; +begin + handlers[TCmdData(p^).cmd.cmd](TCmdData(p^)) +end; + +procedure sendChatLine(msg: PChar); cdecl; +begin + sendNetLn('CHAT'); + sendNet(msg); +end; + +procedure joinRoom(roomName: PChar); cdecl; +begin + sendNetLn('JOIN_ROOM'); + sendNet(roomName); +end; + +procedure partRoom(msg: PChar); cdecl; +var s: string; +begin + if isInRoom then + begin + s:= 'PART'; + if length(msg) > 0 then + s:= s + #10 + msg; + sendNet(s); + + onRoomLeaving() + end +end; + +procedure ResetNetState; +begin + isInRoom:= false; +end; + +end. + diff -r 24f309d75da8 -r 6adf27e85cae hedgewars/uFLNetTypes.pas --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hedgewars/uFLNetTypes.pas Fri Jul 15 22:39:07 2016 +0300 @@ -0,0 +1,57 @@ +unit uFLNetTypes; +interface + +type TCmdType = (cmd_ADD_TEAM, cmd_ADD_TEAM_s, cmd_ASKPASSWORD, cmd_BANLIST, + cmd_BANLIST_s, cmd_BYE, cmd_CFG_AMMO, cmd_CFG_DRAWNMAP, cmd_CFG_FEATURE_SIZE, + cmd_CFG_FULLMAPCONFIG, cmd_CFG_FULLMAPCONFIG_s, cmd_CFG_MAP, cmd_CFG_MAPGEN, + cmd_CFG_MAZE_SIZE, cmd_CFG_SCHEME, cmd_CFG_SCHEME_s, cmd_CFG_SCRIPT, + cmd_CFG_SEED, cmd_CFG_TEMPLATE, cmd_CFG_THEME, cmd_CHAT, cmd_CLIENT_FLAGS, + cmd_CLIENT_FLAGS_s, cmd_CONNECTED, cmd_EM, cmd_EM_s, cmd_ERROR, cmd_HH_NUM, + cmd_INFO, cmd_INFO_s, cmd_JOINED, cmd_JOINED_s, cmd_JOINING, cmd_KICKED, + cmd_LEFT, cmd_LOBBY_JOINED, cmd_LOBBY_JOINED_s, cmd_LOBBY_LEFT, cmd_NICK, + cmd_NOTICE, cmd_PING, cmd_PING_s, cmd_PROTO, cmd_REMOVE_TEAM, cmd_ROOMS, + cmd_ROOMS_s, cmd_ROOM_ADD, cmd_ROOM_ADD_s, cmd_ROOM_DEL, cmd_ROOM_UPD, + cmd_ROOM_UPD_s, cmd_ROUND_FINISHED, cmd_RUN_GAME, cmd_SERVER_AUTH, + cmd_SERVER_MESSAGE, cmd_SERVER_VARS, cmd_TEAM_ACCEPTED, cmd_TEAM_COLOR, + cmd_WARNING); + + type TCmdParam = packed record + cmd: TCmdType; + end; + type TCmdParamL = packed record + cmd: TCmdType; + str1len: Longword; + str1: array[word] of char; + end; + type TCmdParamS = packed record + cmd: TCmdType; + str1: shortstring; + end; + type TCmdParamSL = packed record + cmd: TCmdType; + str1: shortstring; + str2len: Longword; + str2: array[word] of char; + end; + type TCmdParamSS = packed record + cmd: TCmdType; + str1: shortstring; + str2: shortstring; + end; + type TCmdParami = packed record + cmd: TCmdType; + param1: LongInt; + end; + + TCmdData = record + case byte of + 0: (cmd: TCmdParam); + 1: (cpl: TCmdParamL); + 2: (cps: TCmdParamS); + 3: (cpsl: TCmdParamSL); + 4: (cpi: TCmdParami); + end; + +implementation + +end. diff -r 24f309d75da8 -r 6adf27e85cae hedgewars/uFLRunQueue.pas --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hedgewars/uFLRunQueue.pas Fri Jul 15 22:39:07 2016 +0300 @@ -0,0 +1,93 @@ +unit uFLRunQueue; +interface +uses uFLTypes; + +procedure queueExecution(var config: TGameConfig); +procedure passFlibEvent(p: pointer); cdecl; + +implementation +uses uFLGameConfig, hwengine, uFLThemes, uFLUICallback, uFLIPC; + +var runQueue: PGameConfig = nil; + +procedure nextRun; +begin + if runQueue <> nil then + begin + if runQueue^.gameType = gtPreview then + sendUI(mtRenderingPreview, nil, 0); + + ipcRemoveBarrierFromEngineQueue(); + RunEngine(runQueue^.argumentsNumber, @runQueue^.argv); + end +end; + +procedure cleanupConfig; +var t: PGameConfig; +begin + t:= runQueue; + runQueue:= t^.nextConfig; + dispose(t) +end; + +procedure queueExecution(var config: TGameConfig); +var pConfig, t, tt: PGameConfig; + i: Longword; +begin + new(pConfig); + pConfig^:= config; + + with pConfig^ do + begin + nextConfig:= nil; + + for i:= 0 to Pred(MAXARGS) do + begin + if arguments[i][0] = #255 then + arguments[i][255]:= #0 + else + arguments[i][byte(arguments[i][0]) + 1]:= #0; + argv[i]:= @arguments[i][1] + end; + end; + + if runQueue = nil then + begin + runQueue:= pConfig; + + ipcSetEngineBarrier(); + sendConfig(pConfig); + nextRun + end else + begin + t:= runQueue; + while t^.nextConfig <> nil do + begin + if false and (pConfig^.gameType = gtPreview) and (t^.nextConfig^.gameType = gtPreview) and (t <> runQueue) then + begin + tt:= t^.nextConfig; + pConfig^.nextConfig:= tt^.nextConfig; + t^.nextConfig:= pConfig; + dispose(tt); + exit // boo + end; + t:= t^.nextConfig; + end; + + ipcSetEngineBarrier(); + sendConfig(pConfig); + t^.nextConfig:= pConfig + end; +end; + +procedure passFlibEvent(p: pointer); cdecl; +begin + case TFLIBEvent(p^) of + flibGameFinished: begin + cleanupConfig; + nextRun + end; + end; +end; + +end. diff -r 24f309d75da8 -r 6adf27e85cae hedgewars/uFLSchemes.pas --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hedgewars/uFLSchemes.pas Fri Jul 15 22:39:07 2016 +0300 @@ -0,0 +1,246 @@ +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, uFLThemes; + +const MAX_SCHEME_NAMES = 64; +type + TSchemeArray = array [0..0] of TScheme; + PSchemeArray = ^TSchemeArray; +var + schemesList: PScheme; + schemesNumber: LongInt; + listOfSchemeNames: array[0..MAX_SCHEME_NAMES] of PChar; + tmpScheme: TScheme; + +const ints: array[0 .. 17] of record + name: shortstring; + param: ^LongInt; + end = ( + (name: 'damagefactor'; param: @tmpScheme.damagefactor) + , (name: 'turntime'; param: @tmpScheme.turntime) + , (name: 'health'; param: @tmpScheme.health) + , (name: 'suddendeath'; param: @tmpScheme.suddendeath) + , (name: 'caseprobability'; param: @tmpScheme.caseprobability) + , (name: 'minestime'; param: @tmpScheme.minestime) + , (name: 'landadds'; param: @tmpScheme.landadds) + , (name: 'minedudpct'; param: @tmpScheme.minedudpct) + , (name: 'explosives'; param: @tmpScheme.explosives) + , (name: 'minesnum'; param: @tmpScheme.minesnum) + , (name: 'healthprobability'; param: @tmpScheme.healthprobability) + , (name: 'healthcaseamount'; param: @tmpScheme.healthcaseamount) + , (name: 'waterrise'; param: @tmpScheme.waterrise) + , (name: 'healthdecrease'; param: @tmpScheme.healthdecrease) + , (name: 'ropepct'; param: @tmpScheme.ropepct) + , (name: 'getawaytime'; param: @tmpScheme.getawaytime) + , (name: 'worldedge'; param: @tmpScheme.worldedge) + , (name: 'airmines'; param: @tmpScheme.airmines) + ); +const bools: array[0 .. 24] of record + name: shortstring; + param: ^boolean; + flag: Longword; + end = ( + (name: 'fortsmode'; param: @tmpScheme.fortsmode; flag: $00001000) + , (name: 'divteams'; param: @tmpScheme.divteams; flag: $00000010) + , (name: 'solidland'; param: @tmpScheme.solidland; flag: $00000004) + , (name: 'border'; param: @tmpScheme.border; flag: $00000008) + , (name: 'lowgrav'; param: @tmpScheme.lowgrav; flag: $00000020) + , (name: 'laser'; param: @tmpScheme.laser; flag: $00000040) + , (name: 'invulnerability'; param: @tmpScheme.invulnerability; flag: $00000080) + , (name: 'resethealth'; param: @tmpScheme.resethealth; flag: $00000100) + , (name: 'vampiric'; param: @tmpScheme.vampiric; flag: $00000200) + , (name: 'karma'; param: @tmpScheme.karma; flag: $00000400) + , (name: 'artillery'; param: @tmpScheme.artillery; flag: $00000800) + , (name: 'randomorder'; param: @tmpScheme.randomorder; flag: $00002000) + , (name: 'king'; param: @tmpScheme.king; flag: $00004000) + , (name: 'placehog'; param: @tmpScheme.placehog; flag: $00008000) + , (name: 'sharedammo'; param: @tmpScheme.sharedammo; flag: $00010000) + , (name: 'disablegirders'; param: @tmpScheme.disablegirders; flag: $00020000) + , (name: 'disablewind'; param: @tmpScheme.disablewind; flag: $00800000) + , (name: 'morewind'; param: @tmpScheme.morewind; flag: $01000000) + , (name: 'tagteam'; param: @tmpScheme.tagteam; flag: $02000000) + , (name: 'bottomborder'; param: @tmpScheme.bottomborder; flag: $04000000) + , (name: 'disablelandobjects'; param: @tmpScheme.disablelandobjects; flag: $00040000) + , (name: 'aisurvival'; param: @tmpScheme.aisurvival; flag: $00080000) + , (name: 'infattack'; param: @tmpScheme.infattack; flag: $00100000) + , (name: 'resetweps'; param: @tmpScheme.resetweps; flag: $00200000) + , (name: 'perhogammo'; param: @tmpScheme.perhogammo; flag: $00400000) + ); + +procedure loadSchemes; +var f: PFSFile; + 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; + gf: Longword; +begin + with scheme do + begin + if turntime <> 45 then + ipcToEngine('e$turntime ' + inttostr(turntime * 1000)); + if minesnum <> 4 then + ipcToEngine('e$minesnum ' + inttostr(minesnum)); + if damagefactor <> 100 then + ipcToEngine('e$damagepct ' + inttostr(damagefactor)); + if worldedge > 0 then + ipcToEngine('e$worldedge ' + inttostr(worldedge)); + if length(scriptparam) > 0 then + ipcToEngine('e$scriptparam ' + scriptparam); + if suddendeath <> 15 then + ipcToEngine('e$sd_turns ' + inttostr(suddendeath)); + if waterrise <> 47 then + ipcToEngine('e$waterrise ' + inttostr(waterrise)); + if ropepct <> 100 then + ipcToEngine('e$ropepct ' + inttostr(ropepct)); + if getawaytime <> 100 then + ipcToEngine('e$getawaytime ' + inttostr(getawaytime)); + if caseprobability <> 5 then + ipcToEngine('e$casefreq ' + inttostr(caseprobability)); + if healthprobability <> 35 then + ipcToEngine('e$healthprob ' + inttostr(healthprobability)); + if minestime <> 3 then + ipcToEngine('e$minestime ' + inttostr(minestime * 1000)); + if minedudpct <> 0 then + ipcToEngine('e$minedudpct ' + inttostr(minedudpct)); + if explosives <> 2 then + ipcToEngine('e$explosives ' + inttostr(explosives)); + if airmines <> 0 then + ipcToEngine('e$airmines ' + inttostr(airmines)); + if healthcaseamount <> 25 then + ipcToEngine('e$hcaseamount ' + inttostr(healthcaseamount)); + if healthdecrease <> 5 then + ipcToEngine('e$healthdec ' + inttostr(healthdecrease)); + + gf:= 0; + + for i:= Low(bools) to High(bools) do + if PBoolean(bools[i].param - @tmpScheme + @scheme)^ then + gf:= gf or bools[i].flag; + + ipcToEngine('e$gmflags ' + inttostr(gf)); + end +end; + +end. diff -r 24f309d75da8 -r 6adf27e85cae hedgewars/uFLScripts.pas --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hedgewars/uFLScripts.pas Fri Jul 15 22:39:07 2016 +0300 @@ -0,0 +1,128 @@ +unit uFLScripts; +interface +uses uFLTypes; + +function getScriptsList: PPChar; cdecl; +procedure freeScriptsList; + +implementation +uses uFLUtils, uFLIPC, uPhysFSLayer, uFLThemes; + +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 24f309d75da8 -r 6adf27e85cae hedgewars/uFLTeams.pas --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hedgewars/uFLTeams.pas Fri Jul 15 22:39:07 2016 +0300 @@ -0,0 +1,225 @@ +unit uFLTeams; +interface +uses uFLTypes; + +function createRandomTeam: TTeam; +procedure sendTeamConfig(hp: LongInt; var team: TTeam); + +function getTeamsList: PPChar; cdecl; +procedure freeTeamsList; + +function teamByName(s: shortstring): PTeam; + +procedure sendTeam(var team: TTeam); +procedure removeTeam(teamName: shortstring); + +implementation +uses uFLUtils, uFLIPC, uPhysFSLayer, uFLThemes, uFLNet; + +const MAX_TEAM_NAMES = 128; +var + teamsList: PTeam; + teamsNumber: Longword; + listOfTeamNames: array[0..MAX_TEAM_NAMES] of PChar; + + +function createRandomTeam: TTeam; +var t: TTeam; + i: Longword; +begin + with t do + begin + teamName:= 'team' + inttostr(random(100)); + + for i:= 0 to 7 do + with hedgehogs[i] do + begin + name:= 'hedgehog ' + inttostr(i); + hat:= 'NoHat' + end; + + botLevel:= 0; + hogsNumber:= 4 + end; + createRandomTeam:= t +end; + + +procedure sendTeamConfig(hp: LongInt; var team: TTeam); +var i: Longword; +begin + with team do + begin + ipcToEngine('eaddteam ' + colorsSet[color] + ' ' + teamName); + + if extDriven then + ipcToEngine('erdriven'); + + for i:= 0 to Pred(hogsNumber) do + begin + ipcToEngine('eaddhh ' + IntToStr(botLevel) + ' ' + IntToStr(hp) + ' ' + hedgehogs[i].name); + ipcToEngine('ehat ' + hedgehogs[i].hat); + end; + end +end; + + +procedure loadTeam(var team: TTeam; fileName: shortstring); +var f: PFSFile; + section: LongInt; + l: shortstring; +begin + section:= -1; + f:= pfsOpenRead(fileName); + + while (not pfsEOF(f)) do + begin + pfsReadLn(f, l); + + if l = '' then + else if l = '[Team]' then + section:= -2 + else if copy(l, 1, 9) = '[Hedgehog' then + section:= StrToInt(copy(l, 10, 1)) + else if section = -2 then + begin // [Team] + if copy(l, 1, 5) = 'Name=' then + team.teamName:= midStr(l, 6) + else if copy(l, 1, 6) = 'Grave=' then + team.grave:= midStr(l, 7) + else if copy(l, 1, 5) = 'Fort=' then + team.fort:= midStr(l, 6) + else if copy(l, 1, 5) = 'Flag=' then + team.flag:= midStr(l, 6) + else if copy(l, 1, 10) = 'Voicepack=' then + team.voice:= midStr(l, 11) + else if copy(l, 1, 11) = 'Difficulty=' then + team.botLevel:= StrToInt(midStr(l, 12)) + end else if (section >= 0) and (section <= 7) then + begin // [Hedgehog*] + if copy(l, 1, 5) = 'Name=' then + team.hedgehogs[section].name:= midStr(l, 6) + else if copy(l, 1, 4) = 'Hat=' then + team.hedgehogs[section].hat:= midStr(l, 5) + end; + end; + + pfsClose(f) +end; + + +procedure loadTeams; +var filesList, tmp: PPChar; + team: PTeam; + s: shortstring; + l: Longword; +begin + filesList:= pfsEnumerateFiles('/Config/Teams'); + teamsNumber:= 0; + + tmp:= filesList; + while tmp^ <> nil do + begin + s:= shortstring(tmp^); + l:= length(s); + if (l > 4) and (copy(s, l - 3, 4) = '.hwt') then inc(teamsNumber); + inc(tmp) + end; + + // TODO: no teams at all? + teamsList:= GetMem(sizeof(teamsList^) * teamsNumber); + + team:= teamsList; + tmp:= filesList; + while tmp^ <> nil do + begin + s:= shortstring(tmp^); + l:= length(s); + if (l > 4) and (copy(s, l - 3, 4) = '.hwt') then + begin + loadTeam(team^, '/Config/Teams/' + s); + inc(team) + end; + inc(tmp) + end; + + pfsFreeList(filesList) +end; + + +function getTeamsList: PPChar; cdecl; +var i, t, l: Longword; + team: PTeam; +begin + if teamsList = nil then + loadTeams; + + t:= teamsNumber; + if t >= MAX_TEAM_NAMES then + t:= MAX_TEAM_NAMES; + + team:= teamsList; + for i:= 0 to Pred(t) do + begin + l:= length(team^.teamName); + if l >= 255 then l:= 254; + team^.teamName[l + 1]:= #0; + listOfTeamNames[i]:= @team^.teamName[1]; + inc(team) + end; + + listOfTeamNames[t]:= nil; + + getTeamsList:= listOfTeamNames +end; + +function teamByName(s: shortstring): PTeam; +var i: Longword; + team: PTeam; +begin + team:= teamsList; + i:= 0; + while (i < teamsNumber) and (team^.teamName <> s) do + begin + inc(team); + inc(i) + end; + + if i < teamsNumber then teamByName:= team else teamByName:= nil +end; + +procedure freeTeamsList; +begin + if teamsList <> nil then + FreeMem(teamsList, sizeof(teamsList^) * teamsNumber) +end; + +procedure sendTeam(var team: TTeam); +var i: Longword; +begin + with team do + begin + sendNetLn('ADD_TEAM'); + sendNetLn(teamName); + sendNetLn(IntToStr(color)); + sendNetLn(grave); + sendNetLn(fort); + sendNetLn(voice); + sendNetLn(flag); + sendNetLn(IntToStr(botLevel)); + for i := 0 to 7 do + begin + sendNetLn(hedgehogs[i].name); + sendNetLn(hedgehogs[i].hat); + end; + sendNetLn('') + end; +end; + +procedure removeTeam(teamName: shortstring); +begin + sendNetLn('REMOVE_TEAM'); + sendNet(teamName) +end; + +end. diff -r 24f309d75da8 -r 6adf27e85cae hedgewars/uFLThemes.pas --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hedgewars/uFLThemes.pas Fri Jul 15 22:39:07 2016 +0300 @@ -0,0 +1,85 @@ +unit uFLThemes; +interface + +function getThemesList: PPChar; cdecl; +procedure freeThemesList(list: PPChar); cdecl; +function getThemeIcon(themeName: PChar; buffer: PChar; buflen: Longword): Longword; cdecl; + +const colorsSet: array[0..8] of shortstring = ( + '16712196' + , '4817089' + , '1959610' + , '11878895' + , '10526880' + , '2146048' + , '16681742' + , '6239749' + , '16776961'); + +implementation +uses uPhysFSLayer; + +function getThemesList: PPChar; cdecl; +var list, res, tmp: PPChar; + i, size: Longword; +begin + list:= pfsEnumerateFiles('Themes'); + size:= 0; + tmp:= list; + while tmp^ <> nil do + begin + inc(size); + inc(tmp) + end; + + res:= GetMem((3 + size) * sizeof(PChar)); + res^:= PChar(list); + inc(res); + res^:= PChar(res + size + 2); + inc(res); + + getThemesList:= res; + + for i:= 1 to size do + begin + if pfsExists('/Themes/' + shortstring(list^) + '/icon.png') then + begin + res^:= list^; + inc(res) + end; + + inc(list) + end; + + res^:= nil +end; + +procedure freeThemesList(list: PPChar); cdecl; +var listEnd: PPChar; +begin + dec(list); + listEnd:= PPChar(list^); + dec(list); + + pfsFreeList(PPChar(list^)); + freeMem(list, (listEnd - list) * sizeof(PChar)) +end; + +function getThemeIcon(themeName: PChar; buffer: PChar; buflen: Longword): Longword; cdecl; +var s: shortstring; + f: PFSFile; +begin + s:= '/Themes/' + shortstring(themeName) + '/icon@2x.png'; + + f:= pfsOpenRead(s); + + if f = nil then + getThemeIcon:= 0 + else + begin + getThemeIcon:= pfsBlockRead(f, buffer, buflen); + pfsClose(f) + end; +end; + +end. diff -r 24f309d75da8 -r 6adf27e85cae hedgewars/uFLTypes.pas --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hedgewars/uFLTypes.pas Fri Jul 15 22:39:07 2016 +0300 @@ -0,0 +1,142 @@ +unit uFLTypes; +interface +uses SDLh; + +const + MAXARGS = 32; + +type + TMessageType = (mtRenderingPreview, mtPreview, mtPreviewHogCount + , mtAddPlayingTeam, mtRemovePlayingTeam + , mtAddTeam, mtRemoveTeam, mtTeamColor, mtHedgehogsNumber + , mtNetData + , mtFlibEvent, mtConnected, mtDisconnected, mtAddLobbyClient + , mtRemoveLobbyClient, mtLobbyChatLine, mtAddRoomClient + , mtRemoveRoomClient, mtRoomChatLine, mtAddRoom, mtUpdateRoom + , mtRemoveRoom, mtError, mtWarning, mtMoveToLobby, mtMoveToRoom + , mtNickname, mtSeed, mtTheme, mtScript, mtFeatureSize, mtMapGen + , mtMap, mtMazeSize, mtTemplate, mtAmmo, mtScheme); + + TFLIBEvent = (flibGameFinished); + + TClientFlag = (cfReady, cfRegistered, cfInRoom, cfContributor, cfInGame, cfRoomAdmin, cfServerAdmin); + + PIPCMessage = ^TIPCMessage; + TIPCMessage = record + str: shortstring; + len: Longword; + buf: Pointer; + barrier: Longword; + next: PIPCMessage; + end; + PIPCQueue = ^TIPCQueue; + TIPCQueue = record + msg: TIPCMessage; + mut: PSDL_Mutex; + cond: PSDL_Cond; + last: PIPCMessage; + end; + + TIPCCallback = procedure (p: pointer; msg: PChar; len: Longword); + TUICallback = procedure (p: pointer; msgType: TMessageType; msg: PChar; len: Longword); cdecl; + + TGameType = (gtPreview, gtLocal, gtNet); + THedgehog = record + name: shortstring; + hat: shortstring; + end; + TTeam = record + teamName + , flag + , grave + , fort + , voice + , owner: shortstring; + color: Longword; + extDriven: boolean; + botLevel: Longword; + hedgehogs: array[0..7] of THedgehog; + hogsNumber: Longword; + end; + PTeam = ^TTeam; + + TScheme = record + schemeName + , scriptparam : shortstring; + fortsmode + , divteams + , solidland + , border + , lowgrav + , laser + , invulnerability + , mines + , vampiric + , karma + , artillery + , randomorder + , king + , placehog + , sharedammo + , disablegirders + , disablewind + , morewind + , tagteam + , resethealth + , disablelandobjects + , aisurvival + , infattack + , resetweps + , perhogammo + , bottomborder: boolean; + damagefactor + , turntime + , health + , suddendeath + , caseprobability + , minestime + , landadds + , minedudpct + , explosives + , minesnum + , healthprobability + , healthcaseamount + , waterrise + , healthdecrease + , ropepct + , getawaytime + , airmines + , worldedge: LongInt + end; + PScheme = ^TScheme; + TAmmo = record + ammoName: shortstring; + a, b, c, d: shortstring; + end; + PAmmo = ^TAmmo; + + PGameConfig = ^TGameConfig; + TGameConfig = record + seed: shortstring; + theme: shortstring; + script: shortstring; + map: shortstring; + drawnData: array[word] of char; + drawnDataSize: LongWord; + scheme: TScheme; + ammo: TAmmo; + mapgen: LongInt; + featureSize: LongInt; + mazesize: LongInt; + template: LongInt; + gameType: TGameType; + teams: array[0..7] of TTeam; + arguments: array[0..Pred(MAXARGS)] of shortstring; + argv: array[0..Pred(MAXARGS)] of PChar; + argumentsNumber: Longword; + nextConfig: PGameConfig; + end; + +implementation + +end. diff -r 24f309d75da8 -r 6adf27e85cae hedgewars/uFLUICallback.pas --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hedgewars/uFLUICallback.pas Fri Jul 15 22:39:07 2016 +0300 @@ -0,0 +1,48 @@ +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; + isGame: boolean; + +procedure engineMessageCallback(p: pointer; msg: PChar; len: Longword); +begin + if msg^ = 'T' then + begin + inc(msg); + isGame:= msg^ = 'G'; + exit; + end; + + if isGame then + begin + end + else begin + if len = 128 * 256 then + uiCallbackFunction(uiCallbackPointer, mtPreview, msg, len) + else if len = 1 then + uiCallbackFunction(uiCallbackPointer, mtPreviewHogCount, msg, len) + end; +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 24f309d75da8 -r 6adf27e85cae hedgewars/uFLUtils.pas --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hedgewars/uFLUtils.pas Fri Jul 15 22:39:07 2016 +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 24f309d75da8 -r 6adf27e85cae hedgewars/uIO.pas --- a/hedgewars/uIO.pas Sat Jul 09 22:40:44 2016 +0300 +++ b/hedgewars/uIO.pas Fri Jul 15 22:39:07 2016 +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 uFLIPC, uFLTypes, uConsole, uConsts, uVariables, uCommands, uUtils, uDebug; const cSendEmptyPacketTime = 1000; @@ -55,15 +54,14 @@ 2: (str: shortstring); end; -var IPCSock: PTCPSocket; - fds: PSDLNet_SocketSet; +var isPonged: boolean; - SocketString: shortstring; headcmd: PCmd; lastcmd: PCmd; flushDelayTicks: LongWord; + SocketString: shortstring; sendBuffer: record buf: array[0..Pred(cSendBufferSize)] of byte; count: Word; @@ -109,23 +107,6 @@ dispose(tmp) end; -procedure InitIPC; -var ipaddr: TIPAddress; -begin - WriteToConsole('Init SDL_Net... '); - SDLCheck(SDLNet_Init = 0, 'SDLNet_Init', true); - fds:= SDLNet_AllocSocketSet(1); - SDLCheck(fds <> nil, 'SDLNet_AllocSocketSet', true); - WriteLnToConsole(msgOK); - WriteToConsole('Establishing IPC connection to tcp 127.0.0.1:' + IntToStr(ipcPort) + ' '); - {$HINTS OFF} - SDLCheck(SDLNet_ResolveHost(ipaddr, PChar('127.0.0.1'), ipcPort) = 0, 'SDLNet_ResolveHost', true); - {$HINTS ON} - IPCSock:= SDLNet_TCP_Open(ipaddr); - SDLCheck(IPCSock <> nil, 'SDLNet_TCP_Open', true); - WriteLnToConsole(msgOK) -end; - procedure ParseChatCommand(command: shortstring; message: shortstring; messageStartIndex: Byte); var @@ -184,31 +165,37 @@ end; procedure IPCCheckSock; -var i: LongInt; - s: shortstring; +var i, t: LongInt; + msg: TIPCMessage; begin - if IPCSock = nil then - exit; - - fds^.numsockets:= 0; - SDLNet_AddSocket(fds, IPCSock); - - while SDLNet_CheckSockets(fds, 0) > 0 do + while ipcCheckFromFrontend() 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 + msg:= ipcReadFromFrontend(); + if msg.str[0] > #0 then + ParseIPCCommand(msg.str) + else begin + i:= 0; + while (i < msg.len) do begin - ParseIPCCommand(copy(SocketString, 2, byte(SocketString[1]))); - Delete(SocketString, 1, Succ(byte(SocketString[1]))) - end + if LongInt(SocketString[0]) + msg.len - i > 255 then + t:= 255 - byte(SocketString[0]) + else + t:= msg.len - i; + + Move(PByteArray(msg.buf)^[i], SocketString[byte(SocketString[0]) + 1], t); + inc(byte(SocketString[0]), t); + inc(i, t); + + while byte(SocketString[0]) > byte(SocketString[1]) do + begin + ParseIPCCommand(copy(SocketString, 2, byte(SocketString[1]))); + Delete(SocketString, 1, Succ(byte(SocketString[1]))) + end; + end; + + FreeMem(msg.buf, msg.len) end - else - OutError('IPC connection lost', true) - end; + end end; procedure LoadRecordFromFile(fileName: shortstring); @@ -269,18 +256,11 @@ procedure flushBuffer(); begin - if IPCSock <> nil then - begin - SDLNet_TCP_Send(IPCSock, @sendBuffer.buf, sendBuffer.count); - flushDelayTicks:= 0; - sendBuffer.count:= 0 - end + flushDelayTicks:= 0; end; procedure SendIPC(s: shortstring); begin -if IPCSock <> nil then - begin if s[0] > #251 then s[0]:= #251; @@ -289,37 +269,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; @@ -453,13 +418,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); @@ -511,9 +476,6 @@ begin RegisterVariable('fatal', @chFatalError, true ); - IPCSock:= nil; - fds:= nil; - headcmd:= nil; lastcmd:= nil; isPonged:= false; @@ -527,10 +489,6 @@ procedure freeModule; begin while headcmd <> nil do RemoveCmd; - SDLNet_FreeSocketSet(fds); - SDLNet_TCP_Close(IPCSock); - SDLNet_Quit(); - end; end. diff -r 24f309d75da8 -r 6adf27e85cae hedgewars/uInputHandler.pas diff -r 24f309d75da8 -r 6adf27e85cae hedgewars/uLocale.pas --- a/hedgewars/uLocale.pas Sat Jul 09 22:40:44 2016 +0300 +++ b/hedgewars/uLocale.pas Fri Jul 15 22:39:07 2016 +0300 @@ -133,28 +133,9 @@ FormatA:= copy(fmt, 1, i - 1) + arg + FormatA(copy(fmt, i + 2, Length(fmt) - i - 1), arg) end; -{$IFDEF HWLIBRARY} procedure LoadLocaleWrapper(path: pchar; userpath: pchar; filename: pchar); cdecl; export; begin - PathPrefix := Strpas(path); - UserPathPrefix := Strpas(userpath); - - //normally this var set in preInit of engine - allOK := true; - - uVariables.initModule; - - PathPrefix:= PathPrefix + #0; - UserPathPrefix:= UserPathPrefix + #0; - uPhysFSLayer.initModule(@PathPrefix[1], @UserPathPrefix[1]); - PathPrefix:= copy(PathPrefix, 1, length(PathPrefix) - 1); - UserPathPrefix:= copy(UserPathPrefix, 1, length(UserPathPrefix) - 1); - LoadLocale(Strpas(filename)); - - uPhysFSLayer.freeModule; - uVariables.freeModule; end; -{$ENDIF} end. diff -r 24f309d75da8 -r 6adf27e85cae hedgewars/uMisc.pas --- a/hedgewars/uMisc.pas Sat Jul 09 22:40:44 2016 +0300 +++ b/hedgewars/uMisc.pas Fri Jul 15 22:39:07 2016 +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 24f309d75da8 -r 6adf27e85cae hedgewars/uPhysFSLayer.pas --- a/hedgewars/uPhysFSLayer.pas Sat Jul 09 22:40:44 2016 +0300 +++ b/hedgewars/uPhysFSLayer.pas Fri Jul 15 22:39:07 2016 +0300 @@ -25,12 +25,15 @@ function pfsOpenWrite(fname: shortstring): PFSFile; function pfsFlush(f: PFSFile): boolean; 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); procedure pfsWriteLn(f: PFSFile; s: shortstring); 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; function pfsMakeDir(path: shortstring): boolean; @@ -105,6 +108,11 @@ 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))) @@ -196,12 +204,8 @@ fp: PChar; {$ENDIF} begin -{$IFDEF HWLIBRARY} //TODO: http://icculus.org/pipermail/physfs/2011-August/001006.html - cPhysfsId:= shortstring(GetCurrentDir()) + {$IFDEF DARWIN}{$IFNDEF IPHONEOS}'/Hedgewars.app/Contents/MacOS/' + {$ENDIF}{$ENDIF} ' hedgewars'; -{$ELSE} - cPhysfsId:= ParamStr(0); -{$ENDIF} + cPhysfsId:= {$IFDEF DARWIN}{$IFNDEF IPHONEOS}'/Hedgewars.app/Contents/MacOS/' + {$ENDIF}{$ENDIF} ' hedgewars'; i:= PHYSFS_init(Str2PChar(cPhysfsId)); //AddFileLog('[PhysFS] init: ' + inttostr(i)); @@ -225,9 +229,6 @@ hedgewarsMountPackages; - // need access to teams and frontend configs (for bindings) - pfsMountAtRoot(userPrefix); - if cTestLua then begin pfsMountAtRoot(Str2PChar(ExtractFileDir(cScriptName))); diff -r 24f309d75da8 -r 6adf27e85cae hedgewars/uRender.pas diff -r 24f309d75da8 -r 6adf27e85cae hedgewars/uTypes.pas --- a/hedgewars/uTypes.pas Sat Jul 09 22:40:44 2016 +0300 +++ b/hedgewars/uTypes.pas Fri Jul 15 22:39:07 2016 +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 24f309d75da8 -r 6adf27e85cae hedgewars/uUtils.pas --- a/hedgewars/uUtils.pas Sat Jul 09 22:40:44 2016 +0300 +++ b/hedgewars/uUtils.pas Fri Jul 15 22:39:07 2016 +0300 @@ -423,7 +423,7 @@ function Str2PChar(const s: shortstring): PChar; -var i :Integer ; +var i: Integer; begin for i:= 1 to Length(s) do begin diff -r 24f309d75da8 -r 6adf27e85cae hedgewars/uVariables.pas --- a/hedgewars/uVariables.pas Sat Jul 09 22:40:44 2016 +0300 +++ b/hedgewars/uVariables.pas Fri Jul 15 22:39:07 2016 +0300 @@ -38,14 +38,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; @@ -268,11 +265,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 @@ -289,7 +286,8 @@ '/Missions/Maps', // ptMissionMaps '/Graphics/SuddenDeath', // ptSuddenDeath '/Graphics/Buttons', // ptButton - '/Shaders' // ptShaders + '/Shaders', // ptShaders + '/Config' // ptConfig ); var @@ -2490,13 +2488,10 @@ cLocaleFName := 'en.txt'; cFullScreen := false; - UserPathPrefix := ''; - ipcPort := 0; recordFileName := ''; UserNick := ''; cStereoMode := smNone; GrayScale := false; - PathPrefix := './'; GameType := gmtLocal; cOnlyStats := False; cScriptName := ''; diff -r 24f309d75da8 -r 6adf27e85cae misc/OfficialChallenges/racer_#17.hwmap --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/misc/OfficialChallenges/racer_#17.hwmap Fri Jul 15 22:39:07 2016 +0300 @@ -0,0 +1,1 @@ +AAAA4Xic43vHWMfAc4SFi4FrB5DFrsEiy8DCyriegTGGRYeBIYldlIGfkX02A/di9qIO7sWsrxhARMf/K0AWLy9bUwffXiDBEst2qPP/b3bRzQwO7FIXGO3YpUAGmC0GGrVlMbsui9JiEPcSkLgDMn7LJRZWpkwGoIT6JSDxn4HvCWPvZq7NjHWbQc64wX2FSYmB7yVj8w0+VqZ0Bp6TLAybgY5kvcFzhOk0AwMDu+gmkNMuMFYC3ceSw3bhApM7u0AXgwgjbyvDJwZ9Bj5p9tBW/nvsoQwAuyw7Aw== \ No newline at end of file diff -r 24f309d75da8 -r 6adf27e85cae qmlFrontend/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qmlFrontend/CMakeLists.txt Fri Jul 15 22:39:07 2016 +0300 @@ -0,0 +1,27 @@ +cmake_minimum_required(VERSION 2.8.11) + +project(hedgewars) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) +set(CMAKE_AUTOMOC ON) + +find_package(OpenGL) + +find_package(Qt5 COMPONENTS Core Qml Quick Gui) + +qt5_add_resources(qresources qmlFrontend.qrc) + +add_executable(hedgewars WIN32 + main + hwengine + previewimageprovider + themeiconprovider + qtquick2applicationviewer/qtquick2applicationviewer + flib.h + ${qresources} + ) + +include_directories(${OPENGL_INCLUDE_DIR}) + +target_link_libraries(hedgewars Qt5::Core Qt5::Gui Qt5::Quick Qt5::Qml) +install(PROGRAMS "${EXECUTABLE_OUTPUT_PATH}/hedgewars${CMAKE_EXECUTABLE_SUFFIX}" DESTINATION ${target_binary_install_dir}) diff -r 24f309d75da8 -r 6adf27e85cae qmlFrontend/flib.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qmlFrontend/flib.h Fri Jul 15 22:39:07 2016 +0300 @@ -0,0 +1,100 @@ +#ifndef FLIB_H +#define FLIB_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +enum MessageType { + MSG_RENDERINGPREVIEW + , MSG_PREVIEW + , MSG_PREVIEWHOGCOUNT + , MSG_ADDPLAYINGTEAM + , MSG_REMOVEPLAYINGTEAM + , MSG_ADDTEAM + , MSG_REMOVETEAM + , MSG_TEAMCOLOR + , MSG_HEDGEHOGSNUMBER + , MSG_NETDATA + , MSG_FLIBEVENT + , MSG_CONNECTED + , MSG_DISCONNECTED + , MSG_ADDLOBBYCLIENT + , MSG_REMOVELOBBYCLIENT + , MSG_LOBBYCHATLINE + , MSG_ADDROOMCLIENT + , MSG_REMOVEROOMCLIENT + , MSG_ROOMCHATLINE + , MSG_ADDROOM + , MSG_UPDATEROOM + , MSG_REMOVEROOM + , MSG_ERROR + , MSG_WARNING + , MSG_MOVETOLOBBY + , MSG_MOVETOROOM + , MSG_NICKNAME + , MSG_SEED + , MSG_THEME + , MSG_SCRIPT + , MSG_FEATURESIZE + , MSG_MAPGEN + , MSG_MAP + , MSG_MAZESIZE + , MSG_TEMPLATE + , MSG_AMMO + , MSG_SCHEME +}; + +typedef union string255_ + { + struct { + unsigned char s[256]; + }; + struct { + unsigned char len; + unsigned char str[255]; + }; + } string255; + +typedef void RunEngine_t(int argc, const char ** argv); +typedef void registerUIMessagesCallback_t(void * context, void (*)(void * context, MessageType mt, const char * msg, uint32_t len)); +typedef void getPreview_t(); +typedef void runQuickGame_t(); +typedef void runLocalGame_t(); +typedef void resetGameConfig_t(); +typedef void setSeed_t(const char * seed); +typedef char *getSeed_t(); +typedef void setTheme_t(const char * themeName); +typedef void setScript_t(const char * scriptName); +typedef void setScheme_t(const char * schemeName); +typedef void setAmmo_t(const char * ammoName); +typedef void flibInit_t(const char * localPrefix, const char * userPrefix); +typedef void flibFree_t(); +typedef void passNetData_t(const char * data); +typedef void passFlibEvent_t(const char * data); +typedef void sendChatLine_t(const char * msg); +typedef void joinRoom_t(const char * roomName); +typedef void partRoom_t(const char * message); + +typedef char **getThemesList_t(); +typedef void freeThemesList_t(char **list); +typedef uint32_t getThemeIcon_t(char * theme, char * buffer, uint32_t size); + +typedef char **getScriptsList_t(); +typedef char **getSchemesList_t(); +typedef char **getAmmosList_t(); + +typedef char **getTeamsList_t(); +typedef void tryAddTeam_t(const char * teamName); +typedef void tryRemoveTeam_t(const char * teamName); +typedef void changeTeamColor_t(const char * teamName, int32_t dir); + +typedef void connectOfficialServer_t(); + +#ifdef __cplusplus +} +#endif + +#endif // FLIB_H diff -r 24f309d75da8 -r 6adf27e85cae qmlFrontend/hwengine.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qmlFrontend/hwengine.cpp Fri Jul 15 22:39:07 2016 +0300 @@ -0,0 +1,436 @@ +#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; + passFlibEvent_t * flibPassFlibEvent; + sendChatLine_t * flibSendChatLine; + joinRoom_t * flibJoinRoom; + partRoom_t * flibPartRoom; +} + +Q_DECLARE_METATYPE(MessageType) + +HWEngine::HWEngine(QQmlEngine *engine, QObject *parent) : + QObject(parent), + m_engine(engine) +{ + qRegisterMetaType("MessageType"); + +#ifdef Q_OS_WIN + QLibrary hwlib("./libhwengine.dll"); +#else + QLibrary hwlib("./libhwengine.so"); +#endif + + if(!hwlib.load()) + qWarning() << "Engine library not found" << hwlib.errorString(); + + flibRunEngine = (RunEngine_t*) hwlib.resolve("RunEngine"); + flibRegisterUIMessagesCallback = (registerUIMessagesCallback_t*) hwlib.resolve("registerUIMessagesCallback"); + flibGetSeed = (getSeed_t*) hwlib.resolve("getSeed"); + flibGetPreview = (getPreview_t*) hwlib.resolve("getPreview"); + flibRunQuickGame = (runQuickGame_t*) hwlib.resolve("runQuickGame"); + flibRunLocalGame = (runLocalGame_t*) hwlib.resolve("runLocalGame"); + flibInit = (flibInit_t*) hwlib.resolve("flibInit"); + flibFree = (flibFree_t*) hwlib.resolve("flibFree"); + + flibSetSeed = (setSeed_t*) hwlib.resolve("setSeed"); + flibSetTheme = (setTheme_t*) hwlib.resolve("setTheme"); + flibSetScript = (setScript_t*) hwlib.resolve("setScript"); + flibSetScheme = (setScheme_t*) hwlib.resolve("setScheme"); + flibSetAmmo = (setAmmo_t*) hwlib.resolve("setAmmo"); + + flibGetThemesList = (getThemesList_t*) hwlib.resolve("getThemesList"); + flibFreeThemesList = (freeThemesList_t*) hwlib.resolve("freeThemesList"); + flibGetThemeIcon = (getThemeIcon_t*) hwlib.resolve("getThemeIcon"); + + flibGetScriptsList = (getScriptsList_t*) hwlib.resolve("getScriptsList"); + flibGetSchemesList = (getSchemesList_t*) hwlib.resolve("getSchemesList"); + flibGetAmmosList = (getAmmosList_t*) hwlib.resolve("getAmmosList"); + + flibResetGameConfig = (resetGameConfig_t*) hwlib.resolve("resetGameConfig"); + flibGetTeamsList = (getTeamsList_t*) hwlib.resolve("getTeamsList"); + flibTryAddTeam = (tryAddTeam_t*) hwlib.resolve("tryAddTeam"); + flibTryRemoveTeam = (tryRemoveTeam_t*) hwlib.resolve("tryRemoveTeam"); + flibChangeTeamColor = (changeTeamColor_t*) hwlib.resolve("changeTeamColor"); + + flibConnectOfficialServer = (connectOfficialServer_t*) hwlib.resolve("connectOfficialServer"); + flibPassNetData = (passNetData_t*) hwlib.resolve("passNetData"); + flibPassFlibEvent = (passFlibEvent_t*) hwlib.resolve("passFlibEvent"); + flibSendChatLine = (sendChatLine_t*) hwlib.resolve("sendChatLine"); + flibJoinRoom = (joinRoom_t*) hwlib.resolve("joinRoom"); + flibPartRoom = (partRoom_t*) hwlib.resolve("partRoom"); + + flibInit("/usr/home/unC0Rr/Sources/Hedgewars/Hedgewars-GC/share/hedgewars/Data", "/usr/home/unC0Rr/.hedgewars"); + flibRegisterUIMessagesCallback(this, &guiMessagesCallback); + + ThemeIconProvider * themeIcon = (ThemeIconProvider *)m_engine->imageProvider(QLatin1String("theme")); + themeIcon->setFileContentsFunction(flibGetThemeIcon); + + fillModels(); +} + +HWEngine::~HWEngine() +{ + flibFree(); +} + +void HWEngine::getPreview() +{ + flibSetSeed(QUuid::createUuid().toString().toLatin1()); + flibGetPreview(); +} + +void HWEngine::runQuickGame() +{ + flibSetSeed(QUuid::createUuid().toString().toLatin1()); + flibRunQuickGame(); +} + +void HWEngine::runLocalGame() +{ + flibRunLocalGame(); +} + + +static QObject *hwengine_singletontype_provider(QQmlEngine *engine, QJSEngine *scriptEngine) +{ + Q_UNUSED(scriptEngine) + + HWEngine *hwengine = new HWEngine(engine); + return hwengine; +} + +void HWEngine::exposeToQML() +{ + qDebug("HWEngine::exposeToQML"); + qmlRegisterSingletonType("Hedgewars.Engine", 1, 0, "HWEngine", hwengine_singletontype_provider); +} + + +void HWEngine::guiMessagesCallback(void *context, MessageType mt, const char * msg, uint32_t len) +{ + HWEngine * obj = (HWEngine *)context; + QByteArray b = QByteArray(msg, len); + + //qDebug() << "FLIPC in" << mt << " size = " << b.size(); + + QMetaObject::invokeMethod(obj, "engineMessageHandler", Qt::QueuedConnection, Q_ARG(MessageType, mt), Q_ARG(QByteArray, b)); +} + +void HWEngine::engineMessageHandler(MessageType mt, const QByteArray &msg) +{ + switch(mt) + { + case MSG_RENDERINGPREVIEW: { + emit previewIsRendering(); + break; + } + case MSG_PREVIEW: { + PreviewImageProvider * preview = (PreviewImageProvider *)m_engine->imageProvider(QLatin1String("preview")); + preview->setPixmap(msg); + emit previewImageChanged(); + break; + } + case MSG_PREVIEWHOGCOUNT: { + emit previewHogCountChanged((quint8)msg.data()[0]); + break; + } + case MSG_ADDPLAYINGTEAM: { + QStringList l = QString::fromUtf8(msg).split('\n'); + emit playingTeamAdded(l[1], l[0].toInt(), true); + break; + } + case MSG_REMOVEPLAYINGTEAM: { + emit playingTeamRemoved(QString::fromUtf8(msg)); + break; + } + case MSG_ADDTEAM: { + emit localTeamAdded(QString::fromUtf8(msg), 0); + break; + } + case MSG_REMOVETEAM: { + emit localTeamRemoved(QString::fromUtf8(msg)); + break; + } + case MSG_TEAMCOLOR: { + QStringList l = QString::fromUtf8(msg).split('\n'); + emit teamColorChanged(l[0], QColor::fromRgba(l[1].toInt()).name()); + break; + } + case MSG_HEDGEHOGSNUMBER: { + QStringList l = QString::fromUtf8(msg).split('\n'); + emit hedgehogsNumberChanged(l[0], l[1].toInt()); + break; + } + case MSG_NETDATA: { + flibPassNetData(msg.constData()); + break; + } + case MSG_FLIBEVENT: { + flibPassFlibEvent(msg.constData()); + break; + } + case MSG_CONNECTED: { + emit netConnected(); + break; + } + case MSG_DISCONNECTED: { + emit netDisconnected(QString::fromUtf8(msg)); + break; + } + case MSG_ADDLOBBYCLIENT: { + emit lobbyClientAdded(QString::fromUtf8(msg)); + break; + } + case MSG_REMOVELOBBYCLIENT: { + QStringList l = QString::fromUtf8(msg).split('\n'); + if(l.size() < 2) + l.append(""); + emit lobbyClientRemoved(l[0], l[1]); + break; + } + case MSG_LOBBYCHATLINE: { + QStringList l = QString::fromUtf8(msg).split('\n'); + emit lobbyChatLine(l[0], l[1]); + break; + } + case MSG_ADDROOMCLIENT: { + emit roomClientAdded(QString::fromUtf8(msg)); + break; + } + case MSG_REMOVEROOMCLIENT: { + QStringList l = QString::fromUtf8(msg).split('\n'); + if(l.size() < 2) + l.append(""); + emit roomClientRemoved(l[0], l[1]); + break; + } + case MSG_ROOMCHATLINE: { + QStringList l = QString::fromUtf8(msg).split('\n'); + emit roomChatLine(l[0], l[1]); + break; + } + case MSG_ADDROOM: { + QStringList l = QString::fromUtf8(msg).split('\n'); + emit roomAdded(0, l[1], l[2].toInt(), l[3].toInt(), l[4], l[5], l[6], l[7], l[8]); + break; + } + case MSG_UPDATEROOM: { + QStringList l = QString::fromUtf8(msg).split('\n'); + emit roomUpdated(l[0], 0, l[2], l[3].toInt(), l[4].toInt(), l[5], l[6], l[7], l[8], l[9]); + break; + } + case MSG_REMOVEROOM: { + emit roomRemoved(QString::fromUtf8(msg)); + break; + } + case MSG_ERROR: { + emit errorMessage(QString::fromUtf8(msg)); + break; + } + case MSG_WARNING: { + emit warningMessage(QString::fromUtf8(msg)); + break; + } + case MSG_MOVETOLOBBY: { + emit movedToLobby(); + break; + } + case MSG_MOVETOROOM: { + emit movedToRoom(); + break; + } + case MSG_NICKNAME: { + m_myNickname = QString::fromUtf8(msg); + break; + } + case MSG_SEED: { + emit seedChanged(QString::fromUtf8(msg)); + break; + } + case MSG_THEME: { + emit themeChanged(QString::fromUtf8(msg)); + break; + } + case MSG_SCRIPT: { + emit scriptChanged(QString::fromUtf8(msg)); + break; + } + case MSG_FEATURESIZE: { + emit featureSizeChanged(msg.toInt()); + break; + } + case MSG_MAPGEN: { + emit mapGenChanged(msg.toInt()); + break; + } + case MSG_MAP: { + emit mapChanged(QString::fromUtf8(msg)); + break; + } + case MSG_MAZESIZE: { + emit mazeSizeChanged(msg.toInt()); + break; + } + case MSG_TEMPLATE: { + emit templateChanged(msg.toInt()); + break; + } + case MSG_AMMO: { + emit ammoChanged(QString::fromUtf8(msg)); + break; + } + case MSG_SCHEME: { + emit schemeChanged(QString::fromUtf8(msg)); + break; + } + } +} + +QString HWEngine::currentSeed() +{ + return QString::fromLatin1(flibGetSeed()); +} + +void HWEngine::fillModels() +{ + QStringList resultModel; + + char ** themes = flibGetThemesList(); + for (char **i = themes; *i != NULL; i++) + resultModel << QString::fromUtf8(*i); + flibFreeThemesList(themes); + + m_engine->rootContext()->setContextProperty("themesModel", QVariant::fromValue(resultModel)); + + // scripts model + resultModel.clear(); + for (char **i = flibGetScriptsList(); *i != NULL; i++) + resultModel << QString::fromUtf8(*i); + + m_engine->rootContext()->setContextProperty("scriptsModel", QVariant::fromValue(resultModel)); + + // schemes model + resultModel.clear(); + for (char **i = flibGetSchemesList(); *i != NULL; i++) + resultModel << QString::fromUtf8(*i); + + m_engine->rootContext()->setContextProperty("schemesModel", QVariant::fromValue(resultModel)); + + // ammos model + resultModel.clear(); + for (char **i = flibGetAmmosList(); *i != NULL; i++) + resultModel << QString::fromUtf8(*i); + + m_engine->rootContext()->setContextProperty("ammosModel", QVariant::fromValue(resultModel)); +} + +void HWEngine::getTeamsList() +{ + char ** teams = flibGetTeamsList(); + for (char **i = teams; *i != NULL; i++) { + QString team = QString::fromUtf8(*i); + + emit localTeamAdded(team, 0); + } +} + +void HWEngine::tryAddTeam(const QString &teamName) +{ + flibTryAddTeam(teamName.toUtf8().constData()); +} + +void HWEngine::tryRemoveTeam(const QString &teamName) +{ + flibTryRemoveTeam(teamName.toUtf8().constData()); +} + +void HWEngine::resetGameConfig() +{ + flibResetGameConfig(); +} + +void HWEngine::changeTeamColor(const QString &teamName, int dir) +{ + flibChangeTeamColor(teamName.toUtf8().constData(), dir); +} + +void HWEngine::connect(const QString &host, quint16 port) +{ + flibConnectOfficialServer(); +} + +void HWEngine::sendChatMessage(const QString &msg) +{ + flibSendChatLine(msg.toUtf8().constData()); +} + +void HWEngine::joinRoom(const QString &roomName) +{ + flibJoinRoom(roomName.toUtf8().constData()); +} + +void HWEngine::partRoom(const QString &message) +{ + flibPartRoom(message.toUtf8().constData()); +} + +QString HWEngine::myNickname() +{ + return m_myNickname; +} + +void HWEngine::setTheme(const QString &theme) +{ + flibSetTheme(theme.toUtf8().constData()); +} + +void HWEngine::setScript(const QString &script) +{ + flibSetScript(script.toUtf8().constData()); +} + +void HWEngine::setScheme(const QString &scheme) +{ + flibSetScheme(scheme.toUtf8().constData()); +} + +void HWEngine::setAmmo(const QString &ammo) +{ + flibSetAmmo(ammo.toUtf8().constData()); +} diff -r 24f309d75da8 -r 6adf27e85cae qmlFrontend/hwengine.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qmlFrontend/hwengine.h Fri Jul 15 22:39:07 2016 +0300 @@ -0,0 +1,122 @@ +#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); + + Q_INVOKABLE void sendChatMessage(const QString & msg); + + Q_INVOKABLE void joinRoom(const QString & roomName); + Q_INVOKABLE void partRoom(const QString & message); + + Q_INVOKABLE QString myNickname(); + +signals: + void errorMessage(const QString & message); + void warningMessage(const QString & message); + + void previewIsRendering(); + void previewImageChanged(); + void previewHogCountChanged(int count); + void localTeamAdded(const QString & teamName, int aiLevel); + void localTeamRemoved(const QString & teamName); + + void playingTeamAdded(const QString & teamName, int aiLevel, bool isLocal); + void playingTeamRemoved(const QString & teamName); + + void teamColorChanged(const QString & teamName, const QString & colorValue); + void hedgehogsNumberChanged(const QString & teamName, int hedgehogsNumber); + + void netConnected(); + void netDisconnected(const QString & message); + + void lobbyClientAdded(const QString & clientName); + void lobbyClientRemoved(const QString & clientName, const QString & reason); + void lobbyChatLine(const QString & nickname, const QString & line); + + void roomClientAdded(const QString & clientName); + void roomClientRemoved(const QString & clientName, const QString & reason); + void roomChatLine(const QString & nickname, const QString & line); + + void movedToLobby(); + void movedToRoom(); + + void seedChanged(const QString & seed); + void themeChanged(const QString & theme); + void scriptChanged(const QString & script); + void featureSizeChanged(int featureSize); + void mapGenChanged(int mapgen); + void mapChanged(const QString & map); + void mazeSizeChanged(int mazeSize); + void templateChanged(int templ); + void ammoChanged(const QString & ammo); + void schemeChanged(const QString & scheme); + + void roomAdded(quint32 flags + , const QString & name + , int players + , int teams + , const QString & host + , const QString & map + , const QString & script + , const QString & scheme + , const QString & weapons); + void roomUpdated(const QString & name + , quint32 flags + , const QString & newName + , int players + , int teams + , const QString & host + , const QString & map + , const QString & script + , const QString & scheme + , const QString & weapons); + void roomRemoved(const QString & name); + +public slots: + +private: + QQmlEngine * m_engine; + QString m_myNickname; + + static void guiMessagesCallback(void * context, MessageType mt, const char * msg, uint32_t len); + void fillModels(); + +private slots: + void engineMessageHandler(MessageType mt, const QByteArray &msg); +}; + +#endif // HWENGINE_H + diff -r 24f309d75da8 -r 6adf27e85cae qmlFrontend/main.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qmlFrontend/main.cpp Fri Jul 15 22:39:07 2016 +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 24f309d75da8 -r 6adf27e85cae qmlFrontend/previewimageprovider.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qmlFrontend/previewimageprovider.cpp Fri Jul 15 22:39:07 2016 +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 24f309d75da8 -r 6adf27e85cae qmlFrontend/previewimageprovider.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qmlFrontend/previewimageprovider.h Fri Jul 15 22:39:07 2016 +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 24f309d75da8 -r 6adf27e85cae qmlFrontend/qml/qmlFrontend/Chat.qml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qmlFrontend/qml/qmlFrontend/Chat.qml Fri Jul 15 22:39:07 2016 +0300 @@ -0,0 +1,137 @@ +import QtQuick 2.0 +import Hedgewars.Engine 1.0 + +Rectangle { + color: "#15193a" + radius: 8 + border.width: 4 + border.color: "#ea761d" + + ListView { + id: chatLines + x: 0 + width: parent.width - clientsList.width + anchors.top: parent.top + anchors.bottom: input.top + focus: true + clip: true + highlightFollowsCurrentItem: true + + model: ListModel { + id: chatLinesModel + } + + delegate: Rectangle { + id: chatLinesDelegate + height: 24 + width: parent.width + color: "transparent" + + Row { + spacing: 8; + Text { + color: "#ffffa0" + text: nick + + MouseArea { + z: 1 + anchors.fill: parent + onClicked: ; + } + } + Text { + color: "#ffffff" + text: line + + MouseArea { + z: 1 + anchors.fill: parent + onClicked: ; + } + } + } + + } + + function addLine(nickname, line) { + chatLinesModel.append({"nick" : nickname, "line": line}) + if(chatLinesModel.count > 200) + chatLinesModel.remove(0) + chatLines.currentIndex = chatLinesModel.count - 1 + } + } + + TextInput { + id: input + x: 0 + width: chatLines.width + height: 24 + anchors.bottom: parent.bottom + color: "#eccd2f" + + onAccepted: { + HWEngine.sendChatMessage(text) + chatLines.addLine(HWEngine.myNickname(), text) + text = "" + } + } + + ListView { + id: clientsList + x: parent.width - width + width: 100 + height: parent.height + focus: true + clip: true + + model: ListModel { + id: chatClientsModel + } + + delegate: Rectangle { + id: chatClientDelegate + height: 24 + width: parent.width + color: "transparent" + + Row { + Text { + color: "#ffffff" + text: name + + MouseArea { + z: 1 + anchors.fill: parent + onClicked: ; + } + } + } + } + } + + function addChatLine(nickname, line) { + chatLines.addLine(nickname, line) + } + + function addClient(clientName) { + chatClientsModel.append({"isAdmin": false, "name": clientName}) + chatLines.addLine("***", qsTr("%1 joined").arg(clientName)) + } + + function removeClient(clientName, reason) { + var i = chatClientsModel.count - 1; + while ((i >= 0) && (chatClientsModel.get(i).name !== clientName)) --i; + + if(i >= 0) { + chatClientsModel.remove(i, 1); + chatLines.addLine("***", qsTr("%1 quit (%2)").arg(clientName).arg(reason)) + } + } + + function clear() { + chatClientsModel.clear() + chatLinesModel.clear() + } +} + + diff -r 24f309d75da8 -r 6adf27e85cae qmlFrontend/qml/qmlFrontend/Connect.qml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qmlFrontend/qml/qmlFrontend/Connect.qml Fri Jul 15 22:39:07 2016 +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 24f309d75da8 -r 6adf27e85cae qmlFrontend/qml/qmlFrontend/First.qml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qmlFrontend/qml/qmlFrontend/First.qml Fri Jul 15 22:39:07 2016 +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 24f309d75da8 -r 6adf27e85cae qmlFrontend/qml/qmlFrontend/GameConfig.qml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qmlFrontend/qml/qmlFrontend/GameConfig.qml Fri Jul 15 22:39:07 2016 +0300 @@ -0,0 +1,366 @@ +import QtQuick 2.0 +import Hedgewars.Engine 1.0 + + +Rectangle { + Column { + spacing: 8 + + HWButton { + id: btnPreview + // this, as well as the positions of images, seems to have the border thickness (3px) into account + width: 262 + height: 134 + + onClicked: HWEngine.getPreview() + + Connections { + target: HWEngine + onPreviewImageChanged: { + previewImage.visible = true + previewImage.source = "image://preview/image" + previewWaitImage.visible = false + } + onPreviewIsRendering: { + previewImage.source = "" + previewImage.visible = false + previewWaitImage.visible = true + } + onPreviewHogCountChanged: { + hogCount.text = count + } + } + + Image { + id: previewBackground + x: 3 + y: 3 + width: 256 + height: 128 + cache: true + source: "qrc:/res/prevBg.png" + } + + Image { + id: previewImage + x: 3 + y: 3 + width: 256 + height: 128 + cache: false + source: "" + } + + Image { + id: previewWaitImage + x: 117 + y: 53 + width: 26 + height: 26 + cache: true + source: "qrc:/res/iconTime.png" + } + + Image { + id: hogCountBackground + x: 216 + y: 6 + width: 41 + height: 21 + cache: true + source: "qrc:/res/hogCntBg.png" + } + + Text { + id: hogCount + x: 230 + y: 10 + text: "?" + horizontalAlignment: Text.AlignHRight + font.family: "MS Shell Dlg" + font.pixelSize: 12 + color: "#ffcc00" + } + } + + HWComboBox { + id: cbTheme + width: 256 + height: 64 + + model: themesModel + delegate: Rectangle { + height: 25 + width: 100 + color: "transparent" + + property alias itemIconSource: themeIcon.source + property alias itemText: themeName.text + + Row { + Image {id: themeIcon; width: height; height: parent.height; source: "image://theme/" + modelData} + Text {id: themeName; text: modelData } + } + + MouseArea { + z: 1 + anchors.fill: parent + onClicked: { + cbTheme.currentIndex = index + HWEngine.setTheme(themeName.text) + } + } + } + + Connections { + target: HWEngine + onThemeChanged: cbTheme.showItem({"iconSource" : "image://theme/" + theme, "text" : theme}); + } + } + + HWComboBox { + id: cbScript + width: 256 + height: 32 + + model: scriptsModel + delegate: Rectangle { + height: 25 + width: 100 + color: "transparent" + + property string itemIconSource: "" + property alias itemText: scriptName.text + + Row { + //Image {id: themeIcon; width: height; height: parent.height; source: "image://theme/" + modelData} + Text {id: scriptName; text: modelData } + } + + MouseArea { + z: 1 + anchors.fill: parent + onClicked: { + cbScript.currentIndex = index + HWEngine.setScript(scriptName.text) + } + } + } + Connections { + target: HWEngine + onScriptChanged: cbScript.showItem({"iconSource" : "", "text" : script}); + } + } + + HWComboBox { + id: cbScheme + width: 256 + height: 32 + + model: schemesModel + delegate: Rectangle { + height: 25 + width: 100 + color: "transparent" + + property string itemIconSource: "" + property alias itemText: schemeName.text + + Row { + //Image {id: themeIcon; width: height; height: parent.height; source: "image://theme/" + modelData} + Text {id: schemeName; text: modelData } + } + + MouseArea { + z: 1 + anchors.fill: parent + onClicked: { + cbScheme.currentIndex = index + HWEngine.setScheme(schemeName.text) + } + } + } + Connections { + target: HWEngine + onSchemeChanged: cbScheme.showItem({"iconSource" : "", "text" : scheme}); + } + } + + + HWComboBox { + id: cbAmmo + width: 256 + height: 32 + + model: ammosModel + delegate: Rectangle { + height: 25 + width: 100 + color: "transparent" + + property string itemIconSource: "" + property alias itemText: ammoName.text + + Row { + //Image {id: themeIcon; width: height; height: parent.height; source: "image://theme/" + modelData} + Text {id: ammoName; text: modelData } + } + + MouseArea { + z: 1 + anchors.fill: parent + onClicked: { + cbAmmo.currentIndex = index + HWEngine.setAmmo(ammoName.text) + } + } + } + Connections { + target: HWEngine + onAmmoChanged: cbAmmo.showItem({"iconSource" : "", "text" : ammo}); + } + } + } + + ListView { + id: playingTeamsList + x: 440 + y: 16 + width: 100 + height: 192 + highlight: Rectangle { color: "#eaea00"; radius: 4 } + focus: true + clip: true + + model: ListModel { + id: playingTeamsModel + } + + delegate: Rectangle { + id: teamDelegate + height: 24 + width: parent.width + radius: 8 + border.width: 2 + border.color: "#eaea00" + + Row { + Rectangle { + height: 20 + width: height + color: teamColor + border.width: 2 + border.color: "#eaea00" + + MouseArea { + z: 1 + anchors.fill: parent + acceptedButtons: Qt.LeftButton | Qt.RightButton + onClicked: { + if (mouse.button === Qt.LeftButton) + HWEngine.changeTeamColor(name, 1) + else if (mouse.button === Qt.RightButton) + HWEngine.changeTeamColor(name, -1) + } + onWheel: HWEngine.changeTeamColor(name, -wheel.angleDelta.y) + } + } + + Text { + text: name + MouseArea { + z: 1 + anchors.fill: parent + onClicked: HWEngine.tryRemoveTeam(name) + } + } + + Text { + text: hedgehogsNumber + } + } + + + } + + Connections { + target: HWEngine + onPlayingTeamAdded: playingTeamsModel.append({ + "aiLevel": aiLevel + , "name": teamName + , "local": isLocal + , "hedgehogsNumber" : 4 + , "teamColor": "#000000" + }) + onPlayingTeamRemoved: { + var i = playingTeamsModel.count - 1; + while ((i >= 0) && (playingTeamsModel.get(i).name !== teamName)) --i + + if(i >= 0) playingTeamsModel.remove(i, 1) + } + onTeamColorChanged: { + var i = playingTeamsModel.count - 1; + while ((i >= 0) && (playingTeamsModel.get(i).name !== teamName)) --i + + if(i >= 0) playingTeamsModel.setProperty(i, "teamColor", colorValue) + } + onHedgehogsNumberChanged: { + var i = playingTeamsModel.count - 1; + while ((i >= 0) && (playingTeamsModel.get(i).name !== teamName)) --i + + if(i >= 0) playingTeamsModel.setProperty(i, "hedgehogsNumber", hedgehogsNumber) + } + } + } + + ListView { + id: localTeamsList + x: 440 + y: 224 + width: 100 + height: 192 + highlight: Rectangle { color: "#eaea00"; radius: 4 } + focus: true + clip: true + + model: ListModel { + id: localTeamsModel + } + + delegate: Rectangle { + id: localTeamDelegate + height: 24 + width: parent.width + radius: 8 + border.width: 2 + border.color: "#eaea00" + + Row { + Text { text: name } + } + + MouseArea { + z: 1 + anchors.fill: parent + onClicked: HWEngine.tryAddTeam(name) + } + } + + Connections { + target: HWEngine + onLocalTeamAdded: localTeamsModel.append({"aiLevel": aiLevel, "name": teamName}) + onLocalTeamRemoved: { + var i = localTeamsModel.count - 1; + while ((i >= 0) && (localTeamsModel.get(i).name !== teamName)) --i + + if(i >= 0) localTeamsModel.remove(i, 1) + } + } + } + + Component.onCompleted: { + HWEngine.resetGameConfig() + HWEngine.getTeamsList() + HWEngine.getPreview() + } +} diff -r 24f309d75da8 -r 6adf27e85cae qmlFrontend/qml/qmlFrontend/HWButton.qml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qmlFrontend/qml/qmlFrontend/HWButton.qml Fri Jul 15 22:39:07 2016 +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: "#ffcc00" + } + }] +} diff -r 24f309d75da8 -r 6adf27e85cae qmlFrontend/qml/qmlFrontend/HWComboBox.qml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qmlFrontend/qml/qmlFrontend/HWComboBox.qml Fri Jul 15 22:39:07 2016 +0300 @@ -0,0 +1,63 @@ +import QtQuick 2.0 +import QtQuick.Window 2.1 + +HWButton { + property alias model: itemsList.model + property alias delegate: itemsList.delegate + property alias currentIndex: itemsList.currentIndex + + Window { + id: selection + visibility: Window.Hidden + modality: Qt.WindowModal + flags: Qt.Dialog + + ListView { + id: itemsList + x: 0 + y: 64 + anchors.fill: parent + anchors.bottomMargin: 32 + highlight: Rectangle { color: "#eaea00"; radius: 4 } + focus: true + + onCurrentItemChanged: { + cbIcon.source = currentItem.itemIconSource + cbText.text = currentItem.itemText + } + } + + HWButton { + x: parent.width - 32 + y: parent.height - 32 + width: 32 + height: 32 + + onClicked: selection.visibility = Window.Hidden; + } + } + + Row { + anchors.fill: parent + anchors.margins: 4 + + Image { + id: cbIcon + width: height + height: parent.height + } + + Text { + id: cbText + height: parent.height + color: "#f3e520" + } + } + + function showItem(item) { + cbIcon.source = item.iconSource + cbText.text = item.text + } + + onClicked: selection.visibility = Window.Windowed +} diff -r 24f309d75da8 -r 6adf27e85cae qmlFrontend/qml/qmlFrontend/Lobby.qml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qmlFrontend/qml/qmlFrontend/Lobby.qml Fri Jul 15 22:39:07 2016 +0300 @@ -0,0 +1,112 @@ +import QtQuick 2.0 +import Hedgewars.Engine 1.0 + +Rectangle { + ListView { + id: roomsList + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: lobbyChat.top + focus: true + clip: true + + model: ListModel { + id: roomsListModel + } + + delegate: Rectangle { + id: roomDelegate + height: 24 + width: parent.width + color: "transparent" + + Row { + spacing: 8; + Text { + text: name + } + Text { + text: players + " / " + teams + } + Text { + text: host + } + Text { + text: map + } + Text { + text: script + } + Text { + text: scheme + } + Text { + text: weapons + } + } + + MouseArea { + z: 1 + anchors.fill: parent + onDoubleClicked: HWEngine.joinRoom(name); + } + } + + Connections { + target: HWEngine + onRoomAdded: roomsListModel.append({ + "name" : name + , "players": players + , "teams": teams + , "host": host + , "map": map + , "script": script + , "scheme": scheme + , "weapons": weapons + }) + onRoomUpdated: { + var i = roomsListModel.count - 1; + while ((i >= 0) && (roomsListModel.get(i).name !== name)) --i + + if(i >= 0) { + roomsListModel.set(i, { + "name" : newName + , "players": players + , "teams": teams + , "host": host + , "map": map + , "script": script + , "scheme": scheme + , "weapons": weapons + }) + } + } + onRoomRemoved: { + var i = roomsListModel.count - 1; + while ((i >= 0) && (roomsListModel.get(i).name !== name)) --i + + if(i >= 0) roomsListModel.remove(i, 1) + } + } + } + + Chat { + id: lobbyChat; + height: 300 + anchors.top: undefined + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + + Connections { + target: HWEngine + onNetConnected: lobbyChat.clear() + onLobbyChatLine: lobbyChat.addChatLine(nickname, line) + onLobbyClientAdded: lobbyChat.addClient(clientName) + onLobbyClientRemoved: lobbyChat.removeClient(clientName, reason) + } + } +} + + diff -r 24f309d75da8 -r 6adf27e85cae qmlFrontend/qml/qmlFrontend/LocalGame.qml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qmlFrontend/qml/qmlFrontend/LocalGame.qml Fri Jul 15 22:39:07 2016 +0300 @@ -0,0 +1,34 @@ +import QtQuick 2.0 +import Hedgewars.Engine 1.0 + +Rectangle { + HWButton { + id: btnQuickGame + x: 8 + y: 66 + width: 150 + height: 150 + + onClicked: HWEngine.runQuickGame() + } + + HWButton { + id: btnMultiplayer + x: 192 + y: 66 + width: 150 + height: 150 + + onClicked: pages.currentPage = "Multiplayer" + } + + HWButton { + id: btnBack + width: 40 + height: 40 + anchors.bottom: parent.bottom + anchors.left: parent.left + + onClicked: pages.currentPage = "First" + } +} diff -r 24f309d75da8 -r 6adf27e85cae qmlFrontend/qml/qmlFrontend/Multiplayer.qml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qmlFrontend/qml/qmlFrontend/Multiplayer.qml Fri Jul 15 22:39:07 2016 +0300 @@ -0,0 +1,32 @@ +import QtQuick 2.0 +import Hedgewars.Engine 1.0 + +Item { + GameConfig { + anchors.left: parent.left + anchors.top: parent.top + anchors.right: parent.right + anchors.bottom: btnRunGame.top + } + + HWButton { + id: btnBack + width: 40 + height: 40 + anchors.bottom: parent.bottom + anchors.left: parent.left + + onClicked: pages.currentPage = "LocalGame" + } + + HWButton { + id: btnRunGame + width: 40 + height: 40 + anchors.bottom: parent.bottom + anchors.right: parent.right + + onClicked: HWEngine.runLocalGame() + } +} + diff -r 24f309d75da8 -r 6adf27e85cae qmlFrontend/qml/qmlFrontend/Room.qml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qmlFrontend/qml/qmlFrontend/Room.qml Fri Jul 15 22:39:07 2016 +0300 @@ -0,0 +1,38 @@ +import QtQuick 2.0 +import Hedgewars.Engine 1.0 + +Rectangle { + HWButton { + id: btnBack + width: 40 + height: 40 + anchors.left: parent.left + anchors.bottom: parent.bottom + + onClicked: HWEngine.partRoom("") + } + + GameConfig { + id: gameConfig + anchors.left: parent.left + anchors.top: parent.top + anchors.right: parent.right + anchors.bottom: roomChat.top + } + + Chat { + id: roomChat; + x: 0; + width: parent.width; + height: 250; + anchors.bottom: btnBack.top + + Connections { + target: HWEngine + onMovedToRoom: roomChat.clear() + onRoomChatLine: roomChat.addChatLine(nickname, line) + onRoomClientAdded: roomChat.addClient(clientName) + onRoomClientRemoved: roomChat.removeClient(clientName, reason) + } + } +} diff -r 24f309d75da8 -r 6adf27e85cae qmlFrontend/qml/qmlFrontend/main.qml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qmlFrontend/qml/qmlFrontend/main.qml Fri Jul 15 22:39:07 2016 +0300 @@ -0,0 +1,94 @@ +import QtQuick 2.0 +import Hedgewars.Engine 1.0 + +Rectangle { + id: pages + width: 800 + height: 600 + + property variant pagesList : [ + "First" + , "LocalGame" + , "Multiplayer" + , "Connect" + , "Lobby" + , "Room" + ]; + + property string currentPage : "First"; + + Repeater { + id: pagesView + model: pagesList + + function loadPage(page) { + // somehow load the page (when Loader has asynchronous == true) + } + + delegate: Loader { + active: false + asynchronous: false + anchors.fill: parent + visible: (currentPage === modelData) + source: "%1.qml".arg(modelData) + onVisibleChanged: loadIfNotLoaded(); + Component.onCompleted: loadIfNotLoaded(); + + function loadIfNotLoaded () + { + if (visible && !active) + active = true; + } + } + } + + Rectangle { + id: warningsBox + y: parent.height - height + width: parent.width - 120 + height: 80 + anchors.horizontalCenter: parent.horizontalCenter + color: "#7e3232" + border.color: "#d3ec2d" + visible: false + z: 2 + + function showMessage(message) { + msgBox.text = message + visible = true + } + + Text { + id: msgBox + x: 0 + y: 0 + height: parent.height + font.pixelSize: 12 + wrapMode: Text.Wrap + } + HWButton { + id: closeButton + x: parent.width - width + y: 0 + width: 40 + height: 40 + onClicked: warningsBox.visible = false + } + } + + Connections { + target: HWEngine + onNetConnected: { + pagesView.loadPage("Lobby"); + pagesView.loadPage("Room"); + } + onMovedToLobby: currentPage = "Lobby"; + onMovedToRoom: currentPage = "Room"; + onNetDisconnected: { + currentPage = "First"; + warningsBox.showMessage(message); + } + onWarningMessage: warningsBox.showMessage(message); + onErrorMessage: warningsBox.showMessage(message); + } +} diff -r 24f309d75da8 -r 6adf27e85cae qmlFrontend/qmlFrontend.qrc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qmlFrontend/qmlFrontend.qrc Fri Jul 15 22:39:07 2016 +0300 @@ -0,0 +1,18 @@ + + + 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 + qml/qmlFrontend/Chat.qml + qml/qmlFrontend/Room.qml + qml/qmlFrontend/Lobby.qml + qml/qmlFrontend/Multiplayer.qml + res/iconTime.png + res/prevBg.png + res/hogCntBg.png + + diff -r 24f309d75da8 -r 6adf27e85cae qmlFrontend/qtquick2applicationviewer/qtquick2applicationviewer.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qmlFrontend/qtquick2applicationviewer/qtquick2applicationviewer.cpp Fri Jul 15 22:39:07 2016 +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 24f309d75da8 -r 6adf27e85cae qmlFrontend/qtquick2applicationviewer/qtquick2applicationviewer.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qmlFrontend/qtquick2applicationviewer/qtquick2applicationviewer.h Fri Jul 15 22:39:07 2016 +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 24f309d75da8 -r 6adf27e85cae qmlFrontend/qtquick2applicationviewer/qtquick2applicationviewer.pri --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qmlFrontend/qtquick2applicationviewer/qtquick2applicationviewer.pri Fri Jul 15 22:39:07 2016 +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 24f309d75da8 -r 6adf27e85cae qmlFrontend/res/hogCntBg.png Binary file qmlFrontend/res/hogCntBg.png has changed diff -r 24f309d75da8 -r 6adf27e85cae qmlFrontend/res/iconTime.png Binary file qmlFrontend/res/iconTime.png has changed diff -r 24f309d75da8 -r 6adf27e85cae qmlFrontend/res/prevBg.png Binary file qmlFrontend/res/prevBg.png has changed diff -r 24f309d75da8 -r 6adf27e85cae qmlFrontend/themeiconprovider.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qmlFrontend/themeiconprovider.cpp Fri Jul 15 22:39:07 2016 +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 = fileSize ? QImage::fromData(buf) : QImage(16, 16, QImage::Format_ARGB32); + + if (size) + *size = img.size(); + return img; +} diff -r 24f309d75da8 -r 6adf27e85cae qmlFrontend/themeiconprovider.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qmlFrontend/themeiconprovider.h Fri Jul 15 22:39:07 2016 +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 24f309d75da8 -r 6adf27e85cae tools/protocolParser.hs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/protocolParser.hs Fri Jul 15 22:39:07 2016 +0300 @@ -0,0 +1,203 @@ +module Main where + +import Text.PrettyPrint.HughesPJ +import qualified Data.MultiMap as MM +import Data.Maybe +import Data.List +import Data.Char +import qualified Data.Set as Set + +data HWProtocol = Command String [CmdParam] + deriving Show + +instance Ord HWProtocol where + (Command a _) `compare` (Command b _) = a `compare` b +instance Eq HWProtocol where + (Command a _) == (Command b _) = a == b + +data CmdParam = Skip + | SS + | LS + | IntP + | Many [CmdParam] + deriving Show + +data ParseTree = PTPrefix String [ParseTree] + | PTCommand String HWProtocol + deriving Show + +cmd = Command +cmd1 s p = Command s [p] +cmd2 s p1 p2 = Command s [p1, p2] + +cmdName (Command n _) = n + +cmdParams2str (Command _ p) = "TCmdParam" ++ concatMap f p + where + f Skip = "" + f SS = "S" + f LS = "L" + f IntP = "i" + f (Many p) = "" + +cmdParams2handlerType (Command _ p) = "handler_" ++ concatMap f p + where + f Skip = "_" + f SS = "S" + f LS = "L" + f IntP = "i" + f (Many p) = 'M' : concatMap f p + +cmdParams2record cmd@(Command _ p) = renderStyle style{lineLength = 80} $ + text "type " <> text (cmdParams2str cmd) + <> text " = record" $+$ nest 4 ( + vcat (map (uncurry f) $ zip [1..] $ filter isRendered p) + $+$ text "end;") + where + isRendered Skip = False + isRendered (Many _) = False + isRendered _ = True + f n Skip = empty + f n SS = text "str" <> int n <> text ": shortstring;" + f n LS = text "str" <> int n <> text ": longstring;" + f n IntP = text "param" <> int n <> text ": LongInt;" + f _ (Many _) = empty + +commandsDescription = [ + cmd "CONNECTED" [Skip, IntP] + , cmd1 "NICK" SS + , cmd1 "PROTO" IntP + , cmd1 "ASKPASSWORD" SS + , cmd1 "SERVER_AUTH" SS + , cmd1 "JOINING" SS + , cmd1 "TEAM_ACCEPTED" SS + , cmd2 "HH_NUM" SS SS + , cmd2 "TEAM_COLOR" SS SS + , cmd1 "BANLIST" $ Many [SS] + , cmd1 "JOINED" $ Many [SS] + , cmd1 "LOBBY:JOINED" $ Many [SS] + , cmd2 "LOBBY:LEFT" SS LS + , cmd2 "CLIENT_FLAGS" SS $ Many [SS] + , cmd2 "LEFT" SS LS + , cmd1 "SERVER_MESSAGE" LS + , cmd1 "ERROR" LS + , cmd1 "NOTICE" LS + , cmd1 "WARNING" LS + , cmd1 "EM" $ Many [LS] + , cmd1 "PING" $ Many [SS] + , cmd2 "CHAT" SS LS + , cmd2 "SERVER_VARS" SS LS + , cmd2 "BYE" SS LS + , cmd1 "INFO" $ Many [SS] + , cmd1 "ROOM~ADD" $ Many [SS] + , cmd1 "ROOM~UPD" $ Many [SS] + , cmd1 "ROOM~DEL" SS + , cmd1 "ROOMS" $ Many [SS] + , cmd "KICKED" [] + , cmd "RUN_GAME" [] + , cmd "ROUND_FINISHED" [] + , cmd1 "ADD_TEAM" $ Many [SS] + , cmd1 "REMOVE_TEAM" SS + , cmd1 "CFG~MAP" SS + , cmd1 "CFG~SEED" SS + , cmd1 "CFG~SCHEME" $ Many [SS] + , cmd1 "CFG~THEME" SS + , cmd1 "CFG~TEMPLATE" IntP + , cmd1 "CFG~MAPGEN" IntP + , cmd1 "CFG~FEATURE_SIZE" IntP + , cmd1 "CFG~MAZE_SIZE" IntP + , cmd1 "CFG~SCRIPT" SS + , cmd1 "CFG~DRAWNMAP" LS + , cmd2 "CFG~AMMO" SS LS + , cmd1 "CFG~FULLMAPCONFIG" $ Many [SS] + ] + +hasMany = any isMany +isMany (Many _) = True +isMany _ = False + +unknown = Command "__UNKNOWN__" [Many [SS]] +unknowncmd = PTPrefix "$" [PTCommand "$" $ unknown] + +fixName = map fixChar +fixChar c | isLetter c = c + | otherwise = '_' + +groupByFirstChar :: [ParseTree] -> [(Char, [ParseTree])] +groupByFirstChar = MM.assocs . MM.fromList . map breakCmd + where + breakCmd (PTCommand (c:cs) params) = (c, PTCommand cs params) + +makePT cmd@(Command n p) = PTCommand n cmd + +buildParseTree cmds = [PTPrefix "!" $ (bpt $ map makePT cmds) ++ [unknowncmd]] + +bpt :: [ParseTree] -> [ParseTree] +bpt cmds = cmdLeaf emptyNamed + where + emptyNamed = partition (\(_, (PTCommand n _:_)) -> null n) $ groupByFirstChar cmds + buildsub :: (Char, [ParseTree]) -> [ParseTree] -> ParseTree + buildsub (c, cmds) pc = let st = (bpt cmds) ++ pc in if null $ drop 1 st then maybeMerge c st else PTPrefix [c] st + buildsub' = flip buildsub [] + cmdLeaf ([], assocs) = map buildsub' assocs + cmdLeaf ([(c, hwc:assocs1)], assocs2) + | null assocs1 = PTPrefix [c] [hwc] : map buildsub' assocs2 + | otherwise = (buildsub (c, assocs1) [hwc]) : map buildsub' assocs2 + + maybeMerge c cmd@[PTCommand {}] = PTPrefix [c] cmd + maybeMerge c cmd@[PTPrefix s ss] = PTPrefix (c:s) ss + maybeMerge c [] = PTPrefix [c] [] + +dumpTree = vcat . map dt + where + dt (PTPrefix s st) = text s $$ (nest (length s) $ vcat $ map dt st) + dt _ = char '$' + +renderArrays (letters, commands, handlers) = vcat $ punctuate (char '\n') [grr, l, s, c, bodies, structs, realHandlers, realHandlersArray, cmds] + where + maybeQuotes "$" = text "#0" + maybeQuotes "~" = text "#10" + maybeQuotes s = if null $ tail s then quotes $ text s else text s + l = text "const letters: array[0.." <> (int $ length letters - 1) <> text "] of char = " + <> parens (hsep . punctuate comma $ map maybeQuotes letters) <> semi + s = text "const commands: array[0.." <> (int $ length commands - 1) <> text "] of integer = " + <> parens (hsep . punctuate comma $ map text commands) <> semi + c = text "const handlers: array[0.." <> (int $ length fixedNames - 1) <> text "] of PHandler = " + <> parens (hsep . punctuate comma $ map (text . (:) '@') handlerTypes) <> semi + grr = text "const net2cmd: array[0.." <> (int $ length fixedNames - 1) <> text "] of TCmdType = " + <> parens (hsep . punctuate comma $ map (text . (++) "cmd_") $ reverse fixedNames) <> semi + handlerTypes = "handler__UNKNOWN_" : (map cmdParams2handlerType $ reverse sortedCmdDescriptions) + sortedCmdDescriptions = sort commandsDescription + fixedNames = map fixName handlers + bodies = vcat $ punctuate (char '\n') $ map handlerBody $ nub $ sort handlerTypes + handlerBody n = text "procedure " <> text n <> semi + $+$ text "begin" + $+$ text "end" <> semi + cmds = text "type TCmdType = " <> parens (hsep $ punctuate comma $ concatMap (rhentry "cmd_") $ sortedCmdDescriptions) <> semi + structs = vcat (map text . Set.toList . Set.fromList $ map cmdParams2record commandsDescription) + realHandlers = vcat $ punctuate (char '\n') $ map rh $ sortedCmdDescriptions + realHandlersArray = text "const handlers: array[TCmdType] of PHandler = " + <> parens (hsep . punctuate comma . concatMap (map ((<>) (text "PHandler") . parens) . rhentry "@handler_") $ sortedCmdDescriptions) <> semi + +rh cmd@(Command n p) = text "procedure handler_" <> text (fixName n) <> parens (text "var p: " <> text (cmdParams2str cmd)) <> semi + $+$ emptyBody $+$ if hasMany p then vcat [space, text "procedure handler_" <> text (fixName n) <> text "_s" <> parens (text "var s: TCmdParamS") <> semi + , emptyBody] else empty + where + emptyBody = text "begin" $+$ text "end" <> semi + +rhentry prefix cmd@(Command n p) = (text . (++) prefix . fixName . cmdName $ cmd) + : if hasMany p then [text . flip (++) "_s" . (++) prefix . fixName . cmdName $ cmd] else [] + +pas = renderArrays $ buildTables $ buildParseTree commandsDescription + where + buildTables cmds = let (_, _, _, t1, t2, t3) = foldr walk (0, [0], -10, [], [], [[]]) cmds in (tail t1, tail t2, concat t3) + walk (PTCommand _ (Command n params)) (lc, s:sh, pc, tbl1, tbl2, (t3:tbl3)) = + (lc, 1:sh, pc - 1, "#10":tbl1, show pc:tbl2, (n:t3):tbl3) + walk (PTPrefix prefix cmds) l = lvldown $ foldr fpf (foldr walk (lvlup l) cmds) prefix + lvlup (lc, sh, pc, tbl1, tbl2, tbl3) = (lc, 0:sh, pc, tbl1, tbl2, []:tbl3) + lvldown (lc, s1:s2:sh, pc, tbl1, t:tbl2, t31:t32:tbl3) = (lc, s1+s2:sh, pc, tbl1, (if null t32 then "0" else show s1):tbl2, (t31 ++ t32):tbl3) + fpf c (lc, s:sh, pc, tbl1, tbl2, tbl3) = (lc + 1, s+1:sh, pc, [c]:tbl1, "0":tbl2, tbl3) + +main = do + putStrLn $ renderStyle style{mode = ZigZagMode, lineLength = 80} $ pas + --putStrLn $ renderStyle style{lineLength = 80} $ dumpTree $ buildParseTree commandsDescription