Ticket #9: visual_replay_a18_megapatch.patch
File visual_replay_a18_megapatch.patch, 72.0 KB (added by , 9 years ago) |
---|
-
binaries/data/mods/public/gui/gamesetup/gamesetup.js
510 510 511 511 case "chat": 512 512 addChatMessage({ "type": "message", "guid": message.guid, "text": message.text }); 513 513 break; 514 514 515 case "kicked": 516 addChatMessage({ "type": message.ban ? "banned" : "kicked", "username": message.username }); 517 break; 518 515 519 // Singular client to host message 516 520 case "ready": 517 521 g_ReadyChanged -= 1; 518 522 if (g_ReadyChanged < 1 && g_PlayerAssignments[message.guid].player != -1) 519 523 addChatMessage({ "type": "ready", "guid": message.guid, "ready": +message.status == 1 }); … … 1684 1688 } 1685 1689 1686 1690 function submitChatInput() 1687 1691 { 1688 1692 var input = Engine.GetGUIObjectByName("chatInput"); 1689 var text = input.caption; 1690 if (text.length) 1693 var text = input.caption.trim(); 1694 input.caption = ""; 1695 1696 if (!text.length) 1697 return; 1698 1699 if (text.indexOf("/") == 0) 1691 1700 { 1692 Engine.SendNetworkChat(text); 1693 input.caption = ""; 1701 let kick = text.indexOf("/kick ") == 0; 1702 let ban = text.indexOf("/ban ") == 0; 1703 if (kick || ban) 1704 Engine.KickPlayer(text.substr(text.indexOf(" ") + 1), ban); 1705 return; 1694 1706 } 1707 1708 Engine.SendNetworkChat(text); 1695 1709 } 1696 1710 1697 1711 function addChatMessage(msg) 1698 1712 { 1699 1713 var username = ""; … … 1733 1747 case "disconnect": 1734 1748 var formattedUsername = '[color="'+ color +'"]' + username + '[/color]'; 1735 1749 formatted = '[font="sans-bold-13"] ' + sprintf(translate("== %(message)s"), { message: sprintf(translate("%(username)s has left"), { username: formattedUsername }) }) + '[/font]'; 1736 1750 break; 1737 1751 1752 case "kicked": 1753 formatted = '[font="sans-bold-13"] ' + sprintf(translate("== %(message)s"), { message: sprintf(translate("%(username)s has been kicked."), { username: username }) }) + '[/font]'; 1754 break; 1755 1756 case "banned": 1757 formatted = '[font="sans-bold-13"] ' + sprintf(translate("== %(message)s"), { message: sprintf(translate("%(username)s has been banned."), { username: username }) }) + '[/font]'; 1758 break; 1759 1738 1760 case "message": 1739 1761 var formattedUsername = '[color="'+ color +'"]' + username + '[/color]'; 1740 1762 var formattedUsernamePrefix = '[font="sans-bold-13"]' + sprintf(translate("<%(username)s>"), { username: formattedUsername }) + '[/font]' 1741 1763 formatted = sprintf(translate("%(username)s %(message)s"), { username: formattedUsernamePrefix, message: message }); 1742 1764 break; -
binaries/data/mods/public/gui/gamesetup/gamesetup_mp.xml
2 2 3 3 <objects> 4 4 5 5 <script file="gui/common/network.js"/> 6 6 <script file="gui/common/functions_global_object.js"/> 7 <script file="gui/common/functions_utility.js"/> 7 8 <script file="gui/gamesetup/gamesetup_mp.js"/> 8 9 9 10 <!-- Add a translucent black background to fade out the menu page --> 10 11 <object type="image" sprite="ModernFade"/> 11 12 … … 46 47 </object> 47 48 48 49 <object hotkey="confirm" type="button" size="50%+5 100%-45 100%-18 100%-17" style="ModernButtonRed"> 49 50 <translatableAttribute id="caption">Continue</translatableAttribute> 50 51 <action on="Press"> 51 var joinPlayerName = Engine.GetGUIObjectByName("joinPlayerName").caption;52 var joinPlayerName = sanitizePlayerName(Engine.GetGUIObjectByName("joinPlayerName").caption, true, true); 52 53 var joinServer = Engine.GetGUIObjectByName("joinServer").caption; 53 54 if (startJoin(joinPlayerName, joinServer)) 54 55 switchSetupPage("pageJoin", "pageConnecting"); 55 56 </action> 56 57 </object> … … 88 89 </object> 89 90 90 91 <object type="button" size="50%+5 100%-45 100%-18 100%-17" style="ModernButtonRed"> 91 92 <translatableAttribute id="caption">Continue</translatableAttribute> 92 93 <action on="Press"> 93 var hostPlayerName = Engine.GetGUIObjectByName("hostPlayerName").caption;94 var hostPlayerName = sanitizePlayerName(Engine.GetGUIObjectByName("hostPlayerName").caption, true, true); 94 95 var hostServerName = Engine.GetGUIObjectByName("hostServerName").caption; 95 96 if (startHost(hostPlayerName, hostServerName)) 96 97 switchSetupPage("pageHost", "pageConnecting"); 97 98 </action> 98 99 </object> -
binaries/data/mods/public/gui/options/options.js
30 30 [translate("Real Water Depth"), translate("Use actual water depth in rendering calculations"), {"renderer":"WaterRealDepth", "config":"waterrealdepth"}, "boolean"], 31 31 [translate("Water Reflections"), translate("Allow water to reflect a mirror image"), {"renderer":"WaterReflection", "config":"waterreflection"}, "boolean"], 32 32 [translate("Water Refraction"), translate("Use a real water refraction map and not transparency"), {"renderer":"WaterRefraction", "config":"waterrefraction"}, "boolean"], 33 33 [translate("Shadows on Water"), translate("Cast shadows on water"), {"renderer":"WaterShadows", "config":"watershadows"}, "boolean"], 34 34 [translate("VSync"), translate("Run vertical sync to fix screen tearing. REQUIRES GAME RESTART"), {"config":"vsync"}, "boolean"], 35 [translate("Limit FPS in Menus"), translate("Limit frames per second in all menus."), {"config":"gui.menu.limit_fps"}, "boolean"], 35 36 ], 36 37 "soundSetting": 37 38 [ 38 39 [translate("Master Gain"), translate("Master audio gain"), {"config":"sound.mastergain", "function":"Engine.SetMasterGain(Number(this.caption));"}, "number"], 39 40 [translate("Music Gain"), translate("In game music gain"), {"config":"sound.musicgain", "function":"Engine.SetMusicGain(Number(this.caption));"}, "number"], -
binaries/data/mods/public/gui/session/input.js
157 157 else if (placementSupport.mode === "wall") 158 158 { 159 159 if (placementSupport.wallSet && placementSupport.position) 160 160 { 161 161 // Fetch an updated list of snapping candidate entities 162 placementSupport.wallSnapEntities = Engine.PickSimilar FriendlyEntities(162 placementSupport.wallSnapEntities = Engine.PickSimilarPlayerEntities( 163 163 placementSupport.wallSet.templates.tower, 164 164 placementSupport.wallSnapEntitiesIncludeOffscreen, 165 165 true, // require exact template match 166 166 true // include foundations 167 167 ); … … 547 547 switch (ev.type) 548 548 { 549 549 case "mousemotion": 550 550 var rect = updateBandbox(bandbox, ev, false); 551 551 552 var ents = Engine.Pick FriendlyEntitiesInRect(rect[0], rect[1], rect[2], rect[3], Engine.GetPlayerID());552 var ents = Engine.PickPlayerEntitiesInRect(rect[0], rect[1], rect[2], rect[3], Engine.GetPlayerID()); 553 553 var preferredEntities = getPreferredEntities(ents); 554 554 g_Selection.setHighlightList(preferredEntities); 555 555 556 556 return false; 557 557 … … 559 559 if (ev.button == SDL_BUTTON_LEFT) 560 560 { 561 561 var rect = updateBandbox(bandbox, ev, true); 562 562 563 563 // Get list of entities limited to preferred entities 564 var ents = getPreferredEntities(Engine.Pick FriendlyEntitiesInRect(rect[0], rect[1], rect[2], rect[3], Engine.GetPlayerID()));564 var ents = getPreferredEntities(Engine.PickPlayerEntitiesInRect(rect[0], rect[1], rect[2], rect[3], Engine.GetPlayerID())); 565 565 566 566 // Remove the bandbox hover highlighting 567 567 g_Selection.setHighlightList([]); 568 568 569 569 // Update the list of selected units … … 1041 1041 // Select units matching exact template name (same rank) 1042 1042 templateToMatch = GetEntityState(selectedEntity).template; 1043 1043 } 1044 1044 1045 1045 // TODO: Should we handle "control all units" here as well? 1046 ents = Engine.PickSimilar FriendlyEntities(templateToMatch, showOffscreen, matchRank, false);1046 ents = Engine.PickSimilarPlayerEntities(templateToMatch, showOffscreen, matchRank, false); 1047 1047 } 1048 1048 else 1049 1049 { 1050 1050 // It's single click right now but it may become double or triple click 1051 1051 doubleClicked = false; -
binaries/data/mods/public/gui/session/messages.js
236 236 obj.caption = translate("Connection to the server has been authenticated."); 237 237 obj.hidden = false; 238 238 break; 239 239 case "disconnected": 240 240 g_Disconnected = true; 241 obj.caption = translate("Connection to the server has been lost.") + "\n\n" + translate("The game has ended."); 241 obj.caption = translate("Connection to the server has been lost.") + "\n" + 242 sprintf(translate("Reason: %(reason)s."), { reason: getDisconnectReason(message.reason) }) + "\n" + 243 translate("The game has ended."); 242 244 obj.hidden = false; 243 245 break; 244 246 default: 245 247 error("Unrecognised netstatus type '" + message.status + "'"); 246 248 break; … … 314 316 } 315 317 316 318 function submitChatInput() 317 319 { 318 320 var input = Engine.GetGUIObjectByName("chatInput"); 319 var text = input.caption ;321 var text = input.caption.trim(); 320 322 var isCheat = false; 323 321 324 if (text.length) 322 325 { 326 // Parse cheats 323 327 if (!g_IsObserver && g_Players[Engine.GetPlayerID()].cheatsEnabled) 324 328 { 325 329 for each (var cheat in Object.keys(cheats)) 326 330 { 327 331 // Line must start with the cheat. … … 365 369 isCheat = true; 366 370 break; 367 371 } 368 372 } 369 373 374 // Parse direct commands 375 if (text.indexOf("/") == 0) 376 { 377 if (text == "/list") 378 addChatMessage({ "type": "clientlist", "guid": "local"}); 379 else 380 { 381 let kick = text.indexOf("/kick ") == 0; 382 let ban = text.indexOf("/ban ") == 0; 383 if (kick || ban) 384 Engine.KickPlayer(text.substr(text.indexOf(" ") + 1), ban); 385 } 386 } 370 387 // Observers should only send messages to "/all" 371 if (!isCheat && (!g_IsObserver || text.indexOf("/") == -1 || text.indexOf("/all ") == 0))388 else if (!isCheat && (!g_IsObserver || text.indexOf("/") == -1 || text.indexOf("/all ") == 0)) 372 389 { 373 390 if (Engine.GetGUIObjectByName("toggleTeamChat").checked) 374 391 text = "/team " + text; 375 392 376 393 if (g_IsNetworked) -
binaries/data/mods/public/gui/session/minimap_panel.xml
11 11 <object type="button" 12 12 tooltip_style="sessionToolTip" 13 13 hotkey="selection.idleworker" 14 14 > 15 15 <translatableAttribute id="tooltip">Find idle worker</translatableAttribute> 16 <action on="Press">findIdleUnit(["Female", "Trade ", "FishingBoat", "CitizenSoldier", "Healer"]);</action>16 <action on="Press">findIdleUnit(["Female", "Trader", "FishingBoat", "CitizenSoldier", "Healer"]);</action> 17 17 <action on="MouseEnter">Engine.GetGUIObjectByName("idleOverlay").sprite = "stretched:session/minimap-idle-highlight.png";</action> 18 18 <action on="MouseLeave">Engine.GetGUIObjectByName("idleOverlay").sprite = "stretched:session/minimap-idle.png";</action> 19 19 <action on="MouseLeftPress">Engine.GetGUIObjectByName("idleOverlay").sprite = "stretched:session/minimap-idle.png";</action> 20 20 <action on="MouseLeftRelease">Engine.GetGUIObjectByName("idleOverlay").sprite = "stretched:session/minimap-idle-highlight.png";</action> 21 21 </object> -
binaries/data/mods/public/gui/session/session.js
546 546 if (battleState) 547 547 global.music.setState(global.music.states[battleState]); 548 548 } 549 549 } 550 550 551 function onReplayFinished() 552 { 553 closeMenu(); 554 closeOpenDialogs(); 555 pauseGame(); 556 var btCaptions = [translateWithContext("replayFinished", "Yes"), translateWithContext("replayFinished", "No")]; 557 var btCode = [leaveGame, resumeGame]; 558 messageBox(400, 200, translateWithContext("replayFinished", "The replay has finished. Do you want to quit?"), translateWithContext("replayFinished","Confirmation"), 0, btCaptions, btCode); 559 } 560 551 561 /** 552 562 * updates a status bar on the GUI 553 563 * nameOfBar: name of the bar 554 564 * points: points to show 555 565 * maxPoints: max points … … 706 716 Engine.GetGUIObjectByName("resourceFood").caption = Math.floor(playerState.resourceCounts.food); 707 717 Engine.GetGUIObjectByName("resourceWood").caption = Math.floor(playerState.resourceCounts.wood); 708 718 Engine.GetGUIObjectByName("resourceStone").caption = Math.floor(playerState.resourceCounts.stone); 709 719 Engine.GetGUIObjectByName("resourceMetal").caption = Math.floor(playerState.resourceCounts.metal); 710 720 Engine.GetGUIObjectByName("resourcePop").caption = playerState.popCount + "/" + playerState.popLimit; 711 721 Engine.GetGUIObjectByName("population").tooltip = translate("Population (current / limit)") + "\n" 722 + sprintf(translate("Maximum population: %(popCap)s"), { "popCap": playerState.popMax }); 723 712 724 g_IsTrainingBlocked = playerState.trainingBlocked; 713 725 } 714 726 715 727 function selectAndMoveTo(ent) 716 728 { … … 785 797 786 798 // Toggles the display of status bars for all of the player's entities. 787 799 function recalculateStatusBarDisplay() 788 800 { 789 801 if (g_ShowAllStatusBars) 790 var entities = Engine.PickFriendlyEntitiesOnScreen(Engine.GetPlayerID());802 var entities = g_IsObserver ? Engine.PickAllPlayersEntitiesOnScreen() : Engine.PickPlayerEntitiesOnScreen(Engine.GetPlayerID()); 791 803 else 792 804 { 793 805 var selected = g_Selection.toList(); 794 806 for each (var ent in g_Selection.highlighted) 795 807 selected.push(ent); 796 808 797 809 // Remove selected entities from the 'all entities' array, to avoid disabling their status bars. 798 var entities = Engine.GuiInterfaceCall("GetPlayerEntities").filter( 799 function(idx) { return (selected.indexOf(idx) == -1); } 800 ); 810 var entities = Engine.GuiInterfaceCall(g_IsObserver ? "GetAllPlayerEntities" : "GetPlayerEntities").filter(idx => selected.indexOf(idx) == -1); 801 811 } 802 812 803 813 Engine.GuiInterfaceCall("SetStatusBars", { "entities": entities, "enabled": g_ShowAllStatusBars }); 804 814 } 805 806 815 // Update the additional list of entities to be highlighted. 807 816 function updateAdditionalHighlight() 808 817 { 809 818 var entsAdd = []; // list of entities units to be highlighted 810 819 var entsRemove = []; -
binaries/data/mods/public/gui/session/session.xml
5 5 <script file="gui/common/colorFades.js"/> 6 6 <script file="gui/common/functions_civinfo.js"/> 7 7 <script file="gui/common/functions_global_object.js"/> 8 8 <script file="gui/common/functions_utility.js"/> 9 9 <script file="gui/common/l10n.js"/> 10 <script file="gui/common/network.js"/> 10 11 <script file="gui/common/music.js"/> 11 12 <script file="gui/common/timer.js"/> 12 13 <script file="gui/common/tooltips.js"/> 13 14 <!-- load all scripts in this directory --> 14 15 <script directory="gui/session/"/> … … 20 21 21 22 <action on="SimulationUpdate"> 22 23 onSimulationUpdate(); 23 24 </action> 24 25 26 <action on="ReplayFinished"> 27 onReplayFinished(); 28 </action> 29 25 30 <action on="Press"> 26 31 this.hidden = !this.hidden; 27 32 </action> 28 33 29 34 <!-- ================================ ================================ --> -
binaries/data/mods/public/gui/session/top_panel/resource_population.xml
1 1 <?xml version="1.0" encoding="utf-8"?> 2 2 <object name="population" size="370 0 460 100%" type="image" style="resourceCounter" tooltip_style="sessionToolTipBold"> 3 <translatableAttribute id="tooltip">Population (current / limit)</translatableAttribute>4 3 <object size="0 -4 40 34" type="image" sprite="stretched:session/icons/resources/population.png" ghost="true"/> 5 4 <object size="32 0 100% 100%-2" type="text" style="resourceText" name="resourcePop"/> 6 5 </object> -
binaries/data/mods/public/simulation/components/GuiInterface.js
857 857 { 858 858 var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); 859 859 return cmpRangeManager.GetEntitiesByPlayer(player); 860 860 }; 861 861 862 GuiInterface.prototype.GetAllPlayerEntities = function(player) 863 { 864 var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); 865 return cmpRangeManager.GetAllPlayerEntities(player); 866 }; 867 862 868 /** 863 869 * Displays the rally points of a given list of entities (carried in cmd.entities). 864 870 * 865 871 * The 'cmd' object may carry its own x/z coordinate pair indicating the location where the rally point should 866 872 * be rendered, in order to support instantaneously rendering a rally point marker at a specified location … … 1831 1837 1832 1838 "SetSelectionHighlight": 1, 1833 1839 "GetAllBuildableEntities": 1, 1834 1840 "SetStatusBars": 1, 1835 1841 "GetPlayerEntities": 1, 1842 "GetAllPlayerEntities": 1, 1836 1843 "DisplayRallyPoint": 1, 1837 1844 "SetBuildingPlacementPreview": 1, 1838 1845 "SetWallPlacementPreview": 1, 1839 1846 "GetFoundationSnapData": 1, 1840 1847 "PlaySound": 1, -
source/gui/scripting/ScriptFunctions.cpp
60 60 #include "renderer/scripting/JSInterface_Renderer.h" 61 61 #include "simulation2/Simulation2.h" 62 62 #include "simulation2/components/ICmpAIManager.h" 63 63 #include "simulation2/components/ICmpCommandQueue.h" 64 64 #include "simulation2/components/ICmpGuiInterface.h" 65 #include "simulation2/components/ICmpPlayerManager.h" 65 66 #include "simulation2/components/ICmpRangeManager.h" 66 67 #include "simulation2/components/ICmpSelectable.h" 67 68 #include "simulation2/components/ICmpTemplateManager.h" 68 69 #include "simulation2/helpers/Selection.h" 69 70 #include "soundmanager/SoundManager.h" … … 152 153 entity_id_t PickEntityAtPoint(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), int x, int y) 153 154 { 154 155 return EntitySelection::PickEntityAtPoint(*g_Game->GetSimulation2(), *g_Game->GetView()->GetCamera(), x, y, g_Game->GetPlayerID(), false); 155 156 } 156 157 157 std::vector<entity_id_t> Pick FriendlyEntitiesInRect(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), int x0, int y0, int x1, int y1, int player)158 std::vector<entity_id_t> PickPlayerEntitiesInRect(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), int x0, int y0, int x1, int y1, int player) 158 159 { 159 160 return EntitySelection::PickEntitiesInRect(*g_Game->GetSimulation2(), *g_Game->GetView()->GetCamera(), x0, y0, x1, y1, player, false); 160 161 } 161 162 162 std::vector<entity_id_t> Pick FriendlyEntitiesOnScreen(ScriptInterface::CxPrivate* pCxPrivate, int player)163 std::vector<entity_id_t> PickPlayerEntitiesOnScreen(ScriptInterface::CxPrivate* pCxPrivate, int player) 163 164 { 164 return Pick FriendlyEntitiesInRect(pCxPrivate, 0, 0, g_xres, g_yres, player);165 return PickPlayerEntitiesInRect(pCxPrivate, 0, 0, g_xres, g_yres, player); 165 166 } 166 167 167 std::vector<entity_id_t> PickSimilarFriendlyEntities(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), std::string templateName, bool includeOffScreen, bool matchRank, bool allowFoundations) 168 std::vector<entity_id_t> PickAllPlayersEntitiesOnScreen(ScriptInterface::CxPrivate* pCxPrivate) 169 { 170 std::vector<entity_id_t> entities; 171 172 CmpPtr<ICmpPlayerManager> cmpPlayerManager(*g_Game->GetSimulation2(), SYSTEM_ENTITY); 173 174 if (!cmpPlayerManager) 175 return entities; 176 177 i32 numPlayers = cmpPlayerManager->GetNumPlayers(); 178 for (i32 player = 1; player < numPlayers; ++player) 179 { 180 std::vector<entity_id_t> ents = PickPlayerEntitiesOnScreen(pCxPrivate, player); 181 entities.insert(entities.end(), ents.begin(), ents.end()); 182 } 183 return entities; 184 } 185 186 std::vector<entity_id_t> PickSimilarPlayerEntities(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), std::string templateName, bool includeOffScreen, bool matchRank, bool allowFoundations) 168 187 { 169 188 return EntitySelection::PickSimilarEntities(*g_Game->GetSimulation2(), *g_Game->GetView()->GetCamera(), templateName, g_Game->GetPlayerID(), includeOffScreen, matchRank, false, allowFoundations); 170 189 } 171 190 172 191 CFixedVector3D GetTerrainAtScreenPoint(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), int x, int y) … … 346 365 SAFE_DELETE(g_NetServer); 347 366 SAFE_DELETE(g_NetClient); 348 367 SAFE_DELETE(g_Game); 349 368 } 350 369 370 void KickPlayer(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), CStrW playerName, bool ban) 371 { 372 if (g_NetServer) 373 g_NetServer->KickPlayer(playerName, ban); 374 } 375 351 376 JS::Value PollNetworkClient(ScriptInterface::CxPrivate* pCxPrivate) 352 377 { 353 378 if (!g_NetClient) 354 379 return JS::UndefinedValue(); 355 380 … … 931 956 scriptInterface.RegisterFunction<JS::Value, std::wstring, JS::HandleValue, &GuiInterfaceCall>("GuiInterfaceCall"); 932 957 scriptInterface.RegisterFunction<void, JS::HandleValue, &PostNetworkCommand>("PostNetworkCommand"); 933 958 934 959 // Entity picking 935 960 scriptInterface.RegisterFunction<entity_id_t, int, int, &PickEntityAtPoint>("PickEntityAtPoint"); 936 scriptInterface.RegisterFunction<std::vector<entity_id_t>, int, int, int, int, int, &PickFriendlyEntitiesInRect>("PickFriendlyEntitiesInRect"); 937 scriptInterface.RegisterFunction<std::vector<entity_id_t>, int, &PickFriendlyEntitiesOnScreen>("PickFriendlyEntitiesOnScreen"); 938 scriptInterface.RegisterFunction<std::vector<entity_id_t>, std::string, bool, bool, bool, &PickSimilarFriendlyEntities>("PickSimilarFriendlyEntities"); 961 scriptInterface.RegisterFunction<std::vector<entity_id_t>, int, int, int, int, int, &PickPlayerEntitiesInRect>("PickPlayerEntitiesInRect"); 962 scriptInterface.RegisterFunction<std::vector<entity_id_t>, int, &PickPlayerEntitiesOnScreen>("PickPlayerEntitiesOnScreen"); 963 scriptInterface.RegisterFunction<std::vector<entity_id_t>, &PickAllPlayersEntitiesOnScreen>("PickAllPlayersEntitiesOnScreen"); 964 scriptInterface.RegisterFunction<std::vector<entity_id_t>, std::string, bool, bool, bool, &PickSimilarPlayerEntities>("PickSimilarPlayerEntities"); 939 965 scriptInterface.RegisterFunction<CFixedVector3D, int, int, &GetTerrainAtScreenPoint>("GetTerrainAtScreenPoint"); 940 966 941 967 // Network / game setup functions 942 968 scriptInterface.RegisterFunction<void, &StartNetworkGame>("StartNetworkGame"); 943 969 scriptInterface.RegisterFunction<void, JS::HandleValue, int, &StartGame>("StartGame"); 944 970 scriptInterface.RegisterFunction<void, &Script_EndGame>("EndGame"); 945 971 scriptInterface.RegisterFunction<void, std::wstring, &StartNetworkHost>("StartNetworkHost"); 946 972 scriptInterface.RegisterFunction<void, std::wstring, std::string, &StartNetworkJoin>("StartNetworkJoin"); 947 973 scriptInterface.RegisterFunction<void, &DisconnectNetworkGame>("DisconnectNetworkGame"); 974 scriptInterface.RegisterFunction<void, CStrW, bool, &KickPlayer>("KickPlayer"); 948 975 scriptInterface.RegisterFunction<JS::Value, &PollNetworkClient>("PollNetworkClient"); 949 976 scriptInterface.RegisterFunction<void, JS::HandleValue, &SetNetworkGameAttributes>("SetNetworkGameAttributes"); 950 977 scriptInterface.RegisterFunction<void, int, std::string, &AssignNetworkPlayer>("AssignNetworkPlayer"); 951 978 scriptInterface.RegisterFunction<void, std::string, int, &SetNetworkPlayerStatus>("SetNetworkPlayerStatus"); 952 979 scriptInterface.RegisterFunction<void, &ClearAllPlayerReady>("ClearAllPlayerReady"); -
source/main.cpp
41 41 #include "lib/external_libraries/libsdl.h" 42 42 43 43 #include "ps/ArchiveBuilder.h" 44 44 #include "ps/CConsole.h" 45 45 #include "ps/CLogger.h" 46 #include "ps/ConfigDB.h" 46 47 #include "ps/Filesystem.h" 47 48 #include "ps/Game.h" 48 49 #include "ps/Globals.h" 49 50 #include "ps/Hotkey.h" 50 51 #include "ps/Loader.h" … … 303 304 need_update = false; 304 305 // don't use SDL_WaitEvent: don't want the main loop to freeze until app focus is restored 305 306 SDL_Delay(10); 306 307 } 307 308 308 // T ODO: throttling: limit update and render frequency to the minimum.309 // Throttling: limit update and render frequency to the minimum. 309 310 // this is mostly relevant for "inactive" state, so that other windows 310 311 // get enough CPU time, but it's always nice for power+thermal management. 312 const float maxFPSMenu = 50.0; 313 bool limit_fps = false; 314 CFG_GET_VAL("gui.menu.limit_fps", limit_fps); 315 if (limit_fps && (!g_Game || !g_Game->IsGameStarted())) 316 SDL_Delay((1000.0 / maxFPSMenu) - realTimeSinceLastFrame); 311 317 312 318 313 319 // this scans for changed files/directories and reloads them, thus 314 320 // allowing hotloading (changes are immediately assimilated in-game). 315 321 ReloadChangedFiles(); … … 445 451 return; 446 452 447 453 // run non-visual simulation replay if requested 448 454 if (args.Has("replay")) 449 455 { 456 std::string replayFile = args.Get("replay"); 457 if (!FileExists(OsPath(replayFile))) 458 { 459 debug_printf("ERROR: The requested replay file '%s' does not exist!\n", replayFile.c_str()); 460 return; 461 } 462 450 463 Paths paths(args); 451 464 g_VFS = CreateVfs(20 * MiB); 452 465 g_VFS->Mount(L"cache/", paths.Cache(), VFS_MOUNT_ARCHIVABLE); 453 466 MountMods(paths, GetMods(args, INIT_MODS)); 454 467 455 468 { 456 469 CReplayPlayer replay; 457 replay.Load( args.Get("replay"));458 replay.Replay(args.Has("serializationtest") );470 replay.Load(replayFile); 471 replay.Replay(args.Has("serializationtest"), args.Has("ooslog")); 459 472 } 460 473 461 474 g_VFS.reset(); 462 475 463 476 CXeromyces::Terminate(); 464 477 return; 465 478 } 479 480 // If visual replay file does not exist, quit before starting the renderer 481 if (args.Has("replay-visual")) 482 { 483 std::string replayFile = args.Get("replay-visual"); 484 if (!FileExists(OsPath(replayFile))) 485 { 486 debug_printf("ERROR: The requested replay file '%s' does not exist!\n", replayFile.c_str()); 487 return; 488 } 489 } 490 466 491 467 492 // run in archive-building mode if requested 468 493 if (args.Has("archivebuild")) 469 494 { 470 495 Paths paths(args); -
source/network/NetClient.cpp
91 91 92 92 AddTransition(NCS_PREGAME, (uint)NMT_CHAT, NCS_PREGAME, (void*)&OnChat, context); 93 93 AddTransition(NCS_PREGAME, (uint)NMT_READY, NCS_PREGAME, (void*)&OnReady, context); 94 94 AddTransition(NCS_PREGAME, (uint)NMT_GAME_SETUP, NCS_PREGAME, (void*)&OnGameSetup, context); 95 95 AddTransition(NCS_PREGAME, (uint)NMT_PLAYER_ASSIGNMENT, NCS_PREGAME, (void*)&OnPlayerAssignment, context); 96 AddTransition(NCS_PREGAME, (uint)NMT_KICKED, NCS_PREGAME, (void*)&OnKicked, context); 96 97 AddTransition(NCS_PREGAME, (uint)NMT_GAME_START, NCS_LOADING, (void*)&OnGameStart, context); 97 98 AddTransition(NCS_PREGAME, (uint)NMT_JOIN_SYNC_START, NCS_JOIN_SYNCING, (void*)&OnJoinSyncStart, context); 98 99 99 100 AddTransition(NCS_JOIN_SYNCING, (uint)NMT_CHAT, NCS_JOIN_SYNCING, (void*)&OnChat, context); 100 101 AddTransition(NCS_JOIN_SYNCING, (uint)NMT_GAME_SETUP, NCS_JOIN_SYNCING, (void*)&OnGameSetup, context); … … 107 108 AddTransition(NCS_LOADING, (uint)NMT_CHAT, NCS_LOADING, (void*)&OnChat, context); 108 109 AddTransition(NCS_LOADING, (uint)NMT_GAME_SETUP, NCS_LOADING, (void*)&OnGameSetup, context); 109 110 AddTransition(NCS_LOADING, (uint)NMT_PLAYER_ASSIGNMENT, NCS_LOADING, (void*)&OnPlayerAssignment, context); 110 111 AddTransition(NCS_LOADING, (uint)NMT_LOADED_GAME, NCS_INGAME, (void*)&OnLoadedGame, context); 111 112 113 AddTransition(NCS_INGAME, (uint)NMT_KICKED, NCS_INGAME, (void*)&OnKicked, context); 112 114 AddTransition(NCS_INGAME, (uint)NMT_CHAT, NCS_INGAME, (void*)&OnChat, context); 113 115 AddTransition(NCS_INGAME, (uint)NMT_GAME_SETUP, NCS_INGAME, (void*)&OnGameSetup, context); 114 116 AddTransition(NCS_INGAME, (uint)NMT_PLAYER_ASSIGNMENT, NCS_INGAME, (void*)&OnPlayerAssignment, context); 115 117 AddTransition(NCS_INGAME, (uint)NMT_SIMULATION_COMMAND, NCS_INGAME, (void*)&OnInGame, context); 116 118 AddTransition(NCS_INGAME, (uint)NMT_SYNC_ERROR, NCS_INGAME, (void*)&OnInGame, context); … … 582 584 client->m_ClientTurnManager->UpdateFastForward(); 583 585 584 586 return true; 585 587 } 586 588 589 bool CNetClient::OnKicked(void *context, CFsmEvent* event) 590 { 591 ENSURE(event->GetType() == (uint)NMT_KICKED); 592 593 CNetClient* client = (CNetClient*)context; 594 JSContext* cx = client->GetScriptInterface().GetContext(); 595 596 CKickedMessage* message = (CKickedMessage*)event->GetParamRef(); 597 JS::RootedValue msg(cx); 598 client->GetScriptInterface().Eval("({'type':'kicked'})", &msg); 599 client->GetScriptInterface().SetProperty(msg, "username", message->m_Name, false); 600 client->GetScriptInterface().SetProperty(msg, "ban", message->m_Ban, false); 601 client->PushGuiMessage(msg); 602 603 return true; 604 } 605 587 606 bool CNetClient::OnLoadedGame(void* context, CFsmEvent* event) 588 607 { 589 608 ENSURE(event->GetType() == (uint)NMT_LOADED_GAME); 590 609 591 610 CNetClient* client = (CNetClient*)context; -
source/network/NetClient.h
193 193 static bool OnInGame(void* context, CFsmEvent* event); 194 194 static bool OnGameStart(void* context, CFsmEvent* event); 195 195 static bool OnJoinSyncStart(void* context, CFsmEvent* event); 196 196 static bool OnJoinSyncEndCommandBatch(void* context, CFsmEvent* event); 197 197 static bool OnLoadedGame(void* context, CFsmEvent* event); 198 198 static bool OnKicked(void* context, CFsmEvent* event); 199 199 /** 200 200 * Take ownership of a session object, and use it for all network communication. 201 201 */ 202 202 void SetAndOwnSession(CNetClientSession* session); 203 203 -
source/network/NetHost.h
1 /* Copyright (C) 201 1Wildfire Games.1 /* Copyright (C) 2015 Wildfire Games. 2 2 * This file is part of 0 A.D. 3 3 * 4 4 * 0 A.D. is free software: you can redistribute it and/or modify 5 5 * it under the terms of the GNU General Public License as published by 6 6 * the Free Software Foundation, either version 2 of the License, or … … 60 60 enum NetDisconnectReason 61 61 { 62 62 NDR_UNKNOWN = 0, 63 63 NDR_UNEXPECTED_SHUTDOWN, 64 64 NDR_INCORRECT_PROTOCOL_VERSION, 65 NDR_SERVER_ALREADY_IN_GAME 65 NDR_SERVER_ALREADY_IN_GAME, 66 NDR_KICKED, 67 NDR_BANNED 66 68 }; 67 69 68 70 class CNetHost 69 71 { 70 72 public: -
source/network/NetMessage.cpp
133 133 134 134 case NMT_LOADED_GAME: 135 135 pNewMessage = new CLoadedGameMessage; 136 136 break; 137 137 138 case NMT_KICKED: 139 pNewMessage = new CKickedMessage; 140 break; 141 138 142 case NMT_SERVER_HANDSHAKE: 139 143 pNewMessage = new CSrvHandshakeMessage; 140 144 break; 141 145 142 146 case NMT_SERVER_HANDSHAKE_RESPONSE: -
source/network/NetMessages.h
61 61 NMT_GAME_START, 62 62 NMT_END_COMMAND_BATCH, 63 63 NMT_SYNC_CHECK, // OOS-detection hash checking 64 64 NMT_SYNC_ERROR, // OOS-detection error 65 65 NMT_SIMULATION_COMMAND, 66 NMT_LAST // Last message in the list 66 NMT_LAST, // Last message in the list 67 NMT_KICKED 67 68 }; 68 69 69 70 // Authentication result codes 70 71 enum AuthenticateResultCode 71 72 { … … 135 136 136 137 START_NMT_CLASS_(FileTransferRequest, NMT_FILE_TRANSFER_REQUEST) 137 138 NMT_FIELD_INT(m_RequestID, u32, 4) 138 139 END_NMT_CLASS() 139 140 141 START_NMT_CLASS_(Kicked, NMT_KICKED) 142 NMT_FIELD(CStrW, m_Name) 143 NMT_FIELD_INT(m_Ban, bool, 1) 144 END_NMT_CLASS() 145 140 146 START_NMT_CLASS_(FileTransferResponse, NMT_FILE_TRANSFER_RESPONSE) 141 147 NMT_FIELD_INT(m_RequestID, u32, 4) 142 148 NMT_FIELD_INT(m_Length, u32, 4) 143 149 END_NMT_CLASS() 144 150 -
source/network/NetServer.cpp
119 119 120 120 CNetServerWorker::CNetServerWorker(int autostartPlayers) : 121 121 m_AutostartPlayers(autostartPlayers), 122 122 m_Shutdown(false), 123 123 m_ScriptInterface(NULL), 124 m_NextHostID(1), m_Host(NULL), m_ Stats(NULL)124 m_NextHostID(1), m_Host(NULL), m_HostGUID(), m_Stats(NULL) 125 125 { 126 126 m_State = SERVER_STATE_UNCONNECTED; 127 127 128 128 m_ServerTurnManager = NULL; 129 129 … … 603 603 session->SetFirstState(NSS_HANDSHAKE); 604 604 } 605 605 606 606 bool CNetServerWorker::HandleConnect(CNetServerSession* session) 607 607 { 608 CNetServerWorker& server = session->GetServer(); 609 610 // Disconnect banned IPs 611 if (std::find(server.m_BannedIPs.begin(), server.m_BannedIPs.end(), session->GetIPAddress()) != server.m_BannedIPs.end()) 612 { 613 session->Disconnect(NDR_UNKNOWN); 614 return false; 615 } 616 617 // Send handshake challenge 608 618 CSrvHandshakeMessage handshake; 609 619 handshake.m_Magic = PS_PROTOCOL_MAGIC; 610 620 handshake.m_ProtocolVersion = PS_PROTOCOL_VERSION; 611 621 handshake.m_SoftwareVersion = PS_PROTOCOL_VERSION; 612 622 return session->SendMessage(&handshake); … … 614 624 615 625 void CNetServerWorker::OnUserJoin(CNetServerSession* session) 616 626 { 617 627 AddPlayer(session->GetGUID(), session->GetUserName()); 618 628 629 if (m_HostGUID.empty()) 630 m_HostGUID = session->GetGUID(); 631 619 632 CGameSetupMessage gameSetupMessage(GetScriptInterface()); 620 633 gameSetupMessage.m_Data = m_GameAttributes.get(); 621 634 session->SendMessage(&gameSetupMessage); 622 635 623 636 CPlayerAssignmentMessage assignMessage; … … 629 642 { 630 643 RemovePlayer(session->GetGUID()); 631 644 632 645 if (m_ServerTurnManager && session->GetCurrState() != NSS_JOIN_SYNCING) 633 646 m_ServerTurnManager->UninitialiseClient(session->GetHostID()); // TODO: only for non-observers 634 635 // TODO: ought to switch the player controlled by that client636 // back to AI control, or something?637 647 } 638 648 639 649 void CNetServerWorker::AddPlayer(const CStr& guid, const CStrW& name) 640 650 { 641 651 // Find all player IDs in active use; we mustn't give them to a second player (excluding the unassigned ID: -1) … … 706 716 it->second.m_Status = 0; 707 717 708 718 SendPlayerAssignments(); 709 719 } 710 720 721 void CNetServerWorker::KickPlayer(const CStrW& playerName, const bool ban) 722 { 723 // Find the user with that name 724 std::vector<CNetServerSession*>::iterator it = std::find_if(m_Sessions.begin(), m_Sessions.end(), 725 [&](CNetServerSession* session) { return session->GetUserName() == playerName; }); 726 727 // and return if no one or the host has that name 728 if (it == m_Sessions.end() || (*it)->GetGUID() == m_HostGUID) 729 return; 730 731 if (ban) 732 { 733 // Remember name 734 if (std::find(m_BannedPlayers.begin(), m_BannedPlayers.end(), playerName) == m_BannedPlayers.end()) 735 m_BannedPlayers.push_back(playerName); 736 737 // Remember IP address 738 CStr ipAddress = GetPlayerIPAddress(playerName); 739 if (std::find(m_BannedIPs.begin(), m_BannedIPs.end(), ipAddress) == m_BannedIPs.end()) 740 m_BannedIPs.push_back(ipAddress); 741 } 742 743 // Disconnect that user 744 (*it)->Disconnect(NDR_UNKNOWN); 745 746 // Send message notifying other clients 747 CKickedMessage kickedMessage; 748 kickedMessage.m_Name = playerName; 749 kickedMessage.m_Ban = ban; 750 Broadcast(&kickedMessage); 751 } 752 753 CStr CNetServerWorker::GetPlayerIPAddress(const CStrW& playerName) 754 { 755 for (CNetServerSession* session : m_Sessions) 756 if (session->GetUserName() == playerName) 757 return session->GetIPAddress(); 758 return "(error)"; 759 } 760 711 761 void CNetServerWorker::AssignPlayer(int playerID, const CStr& guid) 712 762 { 713 763 // Remove anyone who's already assigned to this player 714 764 for (PlayerAssignmentMap::iterator it = m_PlayerAssignments.begin(); it != m_PlayerAssignments.end(); ++it) 715 765 { … … 763 813 ENSURE(event->GetType() == (uint)NMT_CLIENT_HANDSHAKE); 764 814 765 815 CNetServerSession* session = (CNetServerSession*)context; 766 816 CNetServerWorker& server = session->GetServer(); 767 817 818 // Check protocol version 768 819 CCliHandshakeMessage* message = (CCliHandshakeMessage*)event->GetParamRef(); 769 820 if (message->m_ProtocolVersion != PS_PROTOCOL_VERSION) 770 821 { 771 822 session->Disconnect(NDR_INCORRECT_PROTOCOL_VERSION); 772 823 return false; 773 824 } 774 825 826 // Send handshake response 775 827 CSrvHandshakeResponseMessage handshakeResponse; 776 828 handshakeResponse.m_UseProtocolVersion = PS_PROTOCOL_VERSION; 777 829 handshakeResponse.m_Message = server.m_WelcomeMessage; 778 830 handshakeResponse.m_Flags = 0; 779 831 session->SendMessage(&handshakeResponse); … … 790 842 791 843 CAuthenticateMessage* message = (CAuthenticateMessage*)event->GetParamRef(); 792 844 793 845 CStrW username = server.DeduplicatePlayerName(SanitisePlayerName(message->m_Name)); 794 846 847 // Disconnect banned usernames 848 if (std::find(server.m_BannedPlayers.begin(), server.m_BannedPlayers.end(), username) != server.m_BannedPlayers.end()) 849 { 850 session->Disconnect(NDR_UNKNOWN); 851 return true; 852 } 853 795 854 bool isRejoining = false; 796 855 797 856 if (server.m_State != SERVER_STATE_PREGAME) 798 857 { 799 858 // isRejoining = true; // uncomment this to test rejoining even if the player wasn't connected previously … … 1107 1166 bool CNetServer::SetupConnection() 1108 1167 { 1109 1168 return m_Worker->SetupConnection(); 1110 1169 } 1111 1170 1171 void CNetServer::KickPlayer(const CStrW& playerName, const bool ban) 1172 { 1173 CScopeLock lock(m_Worker->m_WorkerMutex); 1174 m_Worker->KickPlayer(playerName, ban); 1175 } 1176 1112 1177 void CNetServer::AssignPlayer(int playerID, const CStr& guid) 1113 1178 { 1114 1179 CScopeLock lock(m_Worker->m_WorkerMutex); 1115 1180 m_Worker->m_AssignPlayerQueue.push_back(std::make_pair(playerID, guid)); 1116 1181 } -
source/network/NetServer.h
120 120 * The given GUID will be (re)assigned to the given player ID. 121 121 * Any player currently using that ID will be unassigned. 122 122 * The changes will be asynchronously propagated to all clients. 123 123 */ 124 124 void AssignPlayer(int playerID, const CStr& guid); 125 125 126 126 /** 127 127 * Call from the GUI to update the player readiness. 128 128 * The changes will be asynchronously propagated to all clients. 129 129 */ 130 130 void SetPlayerReady(const CStr& guid, int ready); … … 132 132 /** 133 133 * Call from the GUI to set the all player readiness to 0. 134 134 * The changes will be asynchronously propagated to all clients. 135 135 */ 136 136 void ClearAllPlayerReady(); 137 137 138 /** 139 * Disconnects a player from the gamesetup / session. 140 */ 141 void KickPlayer(const CStrW& playerName, const bool ban); 142 138 143 /** 139 144 * Call from the GUI to asynchronously notify all clients that they should start loading the game. 140 145 */ 141 146 void StartGame(); 142 147 … … 181 186 * Send a message to the given network peer. 182 187 */ 183 188 bool SendMessage(ENetPeer* peer, const CNetMessage* message); 184 189 185 190 /** 191 * Disconnected a player from the match / gamesetup and optionally prevents him/her from rejoining. 192 */ 193 void KickPlayer(const CStrW& playerName, const bool ban); 194 195 /** 186 196 * Send a message to all clients who have completed the full connection process 187 197 * (i.e. are in the pre-game or in-game states). 188 198 */ 189 199 bool Broadcast(const CNetMessage* message); 190 200 201 /** 202 * Returns the IP address of the given connected player. 203 */ 204 CStr GetPlayerIPAddress(const CStrW& playerName); 205 191 206 private: 192 207 friend class CNetServer; 193 208 friend class CNetFileReceiveTask_ServerRejoin; 194 209 195 210 CNetServerWorker(int autostartPlayers); … … 243 258 */ 244 259 void SetTurnLength(u32 msecs); 245 260 246 261 void AddPlayer(const CStr& guid, const CStrW& name); 247 262 void RemovePlayer(const CStr& guid); 248 void SetPlayerReady(const CStr& guid, const int ready); 263 void SetPlayerReady(const CStr& guid, const int ready); 264 CStr GetHostGUID(); 249 265 void SendPlayerAssignments(); 250 266 void ClearAllPlayerReady(); 251 267 252 268 void SetupSession(CNetServerSession* session); 253 269 bool HandleConnect(CNetServerSession* session); … … 268 284 269 285 void ConstructPlayerAssignmentMessage(CPlayerAssignmentMessage& message); 270 286 271 287 void HandleMessageReceive(const CNetMessage* message, CNetServerSession* session); 272 288 273 274 289 /** 275 290 * Internal script context for (de)serializing script messages, 276 291 * and for storing game attributes. 277 292 * (TODO: we shouldn't bother deserializing (except for debug printing of messages), 278 293 * we should just forward messages blindly and efficiently.) … … 296 311 NetServerState m_State; 297 312 298 313 CStrW m_ServerName; 299 314 CStrW m_WelcomeMessage; 300 315 316 std::vector<CStr> m_BannedIPs; 317 std::vector<CStrW> m_BannedPlayers; 318 301 319 u32 m_NextHostID; 302 320 303 321 CNetServerTurnManager* m_ServerTurnManager; 304 322 323 CStr m_HostGUID; 324 305 325 /** 306 326 * A copy of all simulation commands received so far, indexed by 307 327 * turn number, to simplify support for rejoining etc. 308 328 * TODO: verify this doesn't use too much RAM. 309 329 */ -
source/network/NetSession.cpp
1 /* Copyright (C) 201 1Wildfire Games.1 /* Copyright (C) 2015 Wildfire Games. 2 2 * This file is part of 0 A.D. 3 3 * 4 4 * 0 A.D. is free software: you can redistribute it and/or modify 5 5 * it under the terms of the GNU General Public License as published by 6 6 * the Free Software Foundation, either version 2 of the License, or … … 173 173 CNetServerSession::CNetServerSession(CNetServerWorker& server, ENetPeer* peer) : 174 174 m_Server(server), m_FileTransferer(this), m_Peer(peer) 175 175 { 176 176 } 177 177 178 CStr CNetServerSession::GetIPAddress() 179 { 180 char ipAddress[256] = "(error)"; 181 enet_address_get_host_ip(&(m_Peer->address), ipAddress, ARRAY_SIZE(ipAddress)); 182 return CStr(ipAddress); 183 } 184 178 185 void CNetServerSession::Disconnect(u32 reason) 179 186 { 180 187 Update((uint)NMT_CONNECTION_LOST, NULL); 181 188 182 189 enet_peer_disconnect(m_Peer, reason); -
source/network/NetSession.h
1 /* Copyright (C) 201 1Wildfire Games.1 /* Copyright (C) 2015 Wildfire Games. 2 2 * This file is part of 0 A.D. 3 3 * 4 4 * 0 A.D. is free software: you can redistribute it and/or modify 5 5 * it under the terms of the GNU General Public License as published by 6 6 * the Free Software Foundation, either version 2 of the License, or … … 121 121 122 122 u32 GetHostID() const { return m_HostID; } 123 123 void SetHostID(u32 id) { m_HostID = id; } 124 124 125 125 /** 126 * Returns the IP address of the client. 127 */ 128 CStr GetIPAddress(); 129 130 /** 126 131 * Sends a disconnection notification to the client, 127 132 * and sends a NMT_CONNECTION_LOST message to the session FSM. 128 133 * The server will receive a disconnection notification after a while. 129 134 * The server will not receive any further messages sent via this session. 130 135 */ -
source/network/NetTurnManager.cpp
1 /* Copyright (C) 201 2Wildfire Games.1 /* Copyright (C) 2015 Wildfire Games. 2 2 * This file is part of 0 A.D. 3 3 * 4 4 * 0 A.D. is free software: you can redistribute it and/or modify 5 5 * it under the terms of the GNU General Public License as published by 6 6 * the Free Software Foundation, either version 2 of the License, or … … 223 223 NETTURN_LOG((L"OnSyncError(%d, %hs)\n", turn, Hexify(expectedHash).c_str())); 224 224 225 225 // Only complain the first time 226 226 if (m_HasSyncError) 227 227 return; 228 m_HasSyncError = true;229 228 230 229 bool quick = !TurnNeedsFullHash(turn); 231 230 std::string hash; 232 bool ok = m_Simulation2.ComputeStateHash(hash, quick); 233 ENSURE(ok); 231 ENSURE(m_Simulation2.ComputeStateHash(hash, quick)); 234 232 235 233 OsPath path = psLogDir()/"oos_dump.txt"; 236 234 std::ofstream file (OsString(path).c_str(), std::ofstream::out | std::ofstream::trunc); 237 235 m_Simulation2.DumpDebugState(file); 238 236 file.close(); 239 237 238 hash = Hexify(hash); 239 const std::string& expectedHashHex = Hexify(expectedHash); 240 241 DisplayOOSError(turn, hash, expectedHashHex, false, &path); 242 } 243 244 void CNetTurnManager::DisplayOOSError(u32 turn, std::string& hash, const std::string& expectedHash, const bool isReplay, OsPath* path = NULL) 245 { 246 m_HasSyncError = true; 247 240 248 std::stringstream msg; 241 msg << "Out of sync on turn " << turn << ": expected hash " << Hexify(expectedHash) << "\n\n"; 242 msg << "Current state: turn " << m_CurrentTurn << ", hash " << Hexify(hash) << "\n\n"; 243 msg << "Dumping current state to " << utf8_from_wstring(path.string()); 249 msg << "Out of sync on turn " << turn << ": expected hash " << expectedHash << "\n"; 250 251 if (expectedHash != hash || m_CurrentTurn != turn) 252 msg << "\nCurrent state: turn " << m_CurrentTurn << ", hash " << hash << "\n\n"; 253 254 if (isReplay) 255 msg << "\nThe current game state is different from the original game state.\n\n"; 256 else 257 { 258 if (expectedHash == hash) 259 msg << "Your game state is identical to the hosts game state.\n\n"; 260 else 261 msg << "Your game state is different from the hosts game state.\n\n"; 262 } 263 264 if (path) 265 msg << "Dumping current state to " << utf8_from_wstring(OsPath(*path).string()); 266 267 LOGERROR("%s", msg.str()); 268 244 269 if (g_GUI) 245 270 g_GUI->DisplayMessageBox(600, 350, L"Sync error", wstring_from_utf8(msg.str())); 246 else247 LOGERROR("%s", msg.str());248 271 } 249 272 250 273 void CNetTurnManager::Interpolate(float simFrameLength, float realFrameLength) 251 274 { 252 275 // TODO: using m_TurnLength might be a bit dodgy when length changes - maybe … … 320 343 void CNetTurnManager::QuickSave() 321 344 { 322 345 TIMER(L"QuickSave"); 323 346 324 347 std::stringstream stream; 325 bool ok = m_Simulation2.SerializeState(stream); 326 if (!ok) 348 if (!m_Simulation2.SerializeState(stream)) 327 349 { 328 350 LOGERROR("Failed to quicksave game"); 329 351 return; 330 352 } 331 353 … … 348 370 LOGERROR("Cannot quickload game - no game was quicksaved"); 349 371 return; 350 372 } 351 373 352 374 std::stringstream stream(m_QuickSaveState); 353 bool ok = m_Simulation2.DeserializeState(stream); 354 if (!ok) 375 if (!m_Simulation2.DeserializeState(stream)) 355 376 { 356 377 LOGERROR("Failed to quickload game"); 357 378 return; 358 379 } 359 380 … … 400 421 { 401 422 bool quick = !TurnNeedsFullHash(turn); 402 423 std::string hash; 403 424 { 404 425 PROFILE3("state hash check"); 405 bool ok = m_Simulation2.ComputeStateHash(hash, quick); 406 ENSURE(ok); 426 ENSURE(m_Simulation2.ComputeStateHash(hash, quick)); 407 427 } 408 428 409 429 NETTURN_LOG((L"NotifyFinishedUpdate(%d, %hs)\n", turn, Hexify(hash).c_str())); 410 430 411 431 m_Replay.Hash(hash, quick); … … 450 470 { 451 471 #if 0 // this hurts performance and is only useful for verifying log replays 452 472 std::string hash; 453 473 { 454 474 PROFILE3("state hash check"); 455 bool ok = m_Simulation2.ComputeStateHash(hash); 456 ENSURE(ok); 475 ENSURE(m_Simulation2.ComputeStateHash(hash)); 457 476 } 458 477 m_Replay.Hash(hash); 459 478 #endif 460 479 } 461 480 462 481 void CNetLocalTurnManager::OnSimulationMessage(CSimulationMessage* UNUSED(msg)) 463 482 { 464 483 debug_warn(L"This should never be called"); 465 484 } 466 485 486 CNetReplayTurnManager::CNetReplayTurnManager(CSimulation2& simulation, IReplayLogger& replay) : 487 CNetLocalTurnManager(simulation, replay) 488 { 489 } 490 491 void CNetReplayTurnManager::StoreReplayCommand(u32 turn, int player, const std::string& command) 492 { 493 // Using the pair we make sure that commands per turn will be processed in the correct order 494 m_ReplayCommands[turn].push_back(std::make_pair(player, command)); 495 } 496 497 void CNetReplayTurnManager::StoreReplayHash(u32 turn, const std::string& hash, bool quick) 498 { 499 m_ReplayHash[turn] = std::make_pair(hash, quick); 500 } 501 502 void CNetReplayTurnManager::StoreReplayTurnLength(u32 turn, u32 turnLength) 503 { 504 m_ReplayTurnLengths[turn] = turnLength; 505 506 // Initialize turn length 507 if (turn == 0) 508 m_TurnLength = m_ReplayTurnLengths[0]; 509 } 510 511 void CNetReplayTurnManager::StoreFinalReplayTurn(u32 turn) 512 { 513 m_FinalReplayTurn = turn; 514 } 515 516 void CNetReplayTurnManager::NotifyFinishedUpdate(u32 turn) 517 { 518 if (turn > m_FinalReplayTurn) 519 return; 467 520 521 debug_printf("Executing turn %d of %d\n", turn, m_FinalReplayTurn); 522 DoTurn(turn); 468 523 524 // Compare hash if it exists in the replay and if we didn't have an oos already 525 if (m_HasSyncError || m_ReplayHash.find(turn) == m_ReplayHash.end()) 526 return; 527 528 std::string expectedHash = m_ReplayHash[turn].first; 529 bool quickHash = m_ReplayHash[turn].second; 530 531 // Compute hash 532 std::string hash; 533 ENSURE(m_Simulation2.ComputeStateHash(hash, quickHash)); 534 hash = Hexify(hash); 535 536 if (hash != expectedHash) 537 DisplayOOSError(turn-1, hash, expectedHash, true); 538 } 539 540 void CNetReplayTurnManager::DoTurn(u32 turn) 541 { 542 // Save turn length 543 m_TurnLength = m_ReplayTurnLengths[turn]; 544 545 // Simulate commands for that turn 546 for(std::vector<std::pair<player_id_t, std::string> >::iterator it = m_ReplayCommands[turn].begin(); it != m_ReplayCommands[turn].end(); ++it) 547 { 548 JS::RootedValue command(m_Simulation2.GetScriptInterface().GetContext()); 549 m_Simulation2.GetScriptInterface().ParseJSON(it->second, &command); 550 AddCommand(m_ClientId, it->first, command, m_CurrentTurn + 1); 551 } 552 } 469 553 470 554 CNetServerTurnManager::CNetServerTurnManager(CNetServerWorker& server) : 471 555 m_NetServer(server), m_ReadyTurn(1), m_TurnLength(DEFAULT_TURN_LENGTH_MP) 472 556 { 473 557 // The first turn we will actually execute is number 2, -
source/network/NetTurnManager.h
1 /* Copyright (C) 201 2Wildfire Games.1 /* Copyright (C) 2015 Wildfire Games. 2 2 * This file is part of 0 A.D. 3 3 * 4 4 * 0 A.D. is free software: you can redistribute it and/or modify 5 5 * it under the terms of the GNU General Public License as published by 6 6 * the Free Software Foundation, either version 2 of the License, or … … 17 17 18 18 #ifndef INCLUDED_NETTURNMANAGER 19 19 #define INCLUDED_NETTURNMANAGER 20 20 21 21 #include "simulation2/helpers/SimulationCommand.h" 22 #include "lib/os_path.h" 22 23 23 24 #include <list> 24 25 #include <map> 26 #include <vector> 25 27 26 28 class CNetServerWorker; 27 29 class CNetClient; 28 30 class CSimulationMessage; 29 31 class CSimulation2; … … 106 108 * Called when there has been an out-of-sync error. 107 109 */ 108 110 virtual void OnSyncError(u32 turn, const std::string& expectedHash); 109 111 110 112 /** 113 * Shows a message box when an out of sync error has been detected in the session or visual replay. 114 */ 115 virtual void DisplayOOSError(u32 turn, std::string& hash, const std::string& expectedHash, const bool isReplay, OsPath* path); 116 117 /** 111 118 * Called by simulation code, to add a new command to be distributed to all clients and executed soon. 112 119 */ 113 120 virtual void PostCommand(JS::HandleValue data) = 0; 114 121 115 122 /** … … 187 194 std::list<std::string> m_TimeWarpStates; 188 195 std::string m_QuickSaveState; // TODO: should implement a proper disk-based quicksave system 189 196 std::string m_QuickSaveMetadata; 190 197 }; 191 198 199 192 200 /** 193 201 * Implementation of CNetTurnManager for network clients. 194 202 */ 195 203 class CNetClientTurnManager : public CNetTurnManager 196 204 { … … 231 239 232 240 virtual void NotifyFinishedUpdate(u32 turn); 233 241 }; 234 242 235 243 244 245 /** 246 * Implementation of CNetTurnManager for replay games. 247 */ 248 class CNetReplayTurnManager : public CNetLocalTurnManager 249 { 250 public: 251 CNetReplayTurnManager(CSimulation2& simulation, IReplayLogger& replay); 252 253 void StoreReplayCommand(u32 turn, int player, const std::string& command); 254 255 void StoreReplayTurnLength(u32 turn, u32 turnLength); 256 257 void StoreReplayHash(u32 turn, const std::string& hash, bool quick); 258 259 void StoreFinalReplayTurn(u32 turn); 260 261 262 protected: 263 virtual void NotifyFinishedUpdate(u32 turn); 264 265 void DoTurn(u32 turn); 266 267 // Contains the commands of every player on each turn 268 std::map<u32, std::vector<std::pair<player_id_t, std::string> > > m_ReplayCommands; 269 270 // Contains the length of every turn 271 std::map<u32, u32> m_ReplayTurnLengths; 272 273 // Contains all replay hash values and weather or not the quick hash method was used 274 std::map<u32, std::pair<std::string, bool> > m_ReplayHash; 275 276 // The number of the last turn in the replay 277 u32 m_FinalReplayTurn; 278 }; 236 279 /** 237 280 * The server-side counterpart to CNetClientTurnManager. 238 281 * Records the turn state of each client, and sends turn advancement messages 239 282 * when all clients are ready. 240 283 * -
source/ps/Game.cpp
61 61 62 62 /** 63 63 * Constructor 64 64 * 65 65 **/ 66 CGame::CGame(bool disableGraphics ):66 CGame::CGame(bool disableGraphics, bool replayLog): 67 67 m_World(new CWorld(this)), 68 68 m_Simulation2(new CSimulation2(&m_World->GetUnitManager(), g_ScriptRuntime, m_World->GetTerrain())), 69 69 m_GameView(disableGraphics ? NULL : new CGameView(this)), 70 70 m_GameStarted(false), 71 71 m_Paused(false), 72 72 m_SimRate(1.0f), 73 73 m_PlayerID(-1), 74 m_IsSavedGame(false) 74 m_IsSavedGame(false), 75 m_IsReplay(false), 76 m_ReplayStream(NULL) 75 77 { 76 m_ReplayLogger = new CReplayLogger(m_Simulation2->GetScriptInterface());77 78 // TODO: should use CDummyReplayLogger unless activated by cmd-line arg, perhaps? 79 if (replayLog) 80 m_ReplayLogger = new CReplayLogger(m_Simulation2->GetScriptInterface()); 81 else 82 m_ReplayLogger = new CDummyReplayLogger(); 78 83 79 84 // Need to set the CObjectManager references after various objects have 80 85 // been initialised, so do it here rather than via the initialisers above. 81 86 if (m_GameView) 82 87 m_World->GetUnitManager().SetObjectManager(m_GameView->GetObjectManager()); … … 99 104 delete m_TurnManager; 100 105 delete m_GameView; 101 106 delete m_Simulation2; 102 107 delete m_World; 103 108 delete m_ReplayLogger; 109 delete m_ReplayStream; 104 110 } 105 111 106 112 void CGame::SetTurnManager(CNetTurnManager* turnManager) 107 113 { 108 114 if (m_TurnManager) … … 112 118 113 119 if (m_TurnManager) 114 120 m_TurnManager->SetPlayerID(m_PlayerID); 115 121 } 116 122 123 int CGame::LoadReplayData() 124 { 125 ENSURE(m_IsReplay); 126 ENSURE(!m_ReplayPath.empty()); 127 128 CNetReplayTurnManager* replayTurnMgr = static_cast<CNetReplayTurnManager*>(GetTurnManager()); 129 130 u32 currentTurn = 0; 131 std::string type; 132 while ((*m_ReplayStream >> type).good()) 133 { 134 if (type == "turn") 135 { 136 u32 turn = 0; 137 u32 turnLength = 0; 138 *m_ReplayStream >> turn >> turnLength; 139 ENSURE(turn == currentTurn); 140 replayTurnMgr->StoreReplayTurnLength(currentTurn, turnLength); 141 } 142 else if (type == "cmd") 143 { 144 player_id_t player; 145 *m_ReplayStream >> player; 146 147 std::string line; 148 std::getline(*m_ReplayStream, line); 149 replayTurnMgr->StoreReplayCommand(currentTurn, player, line); 150 } 151 else if (type == "hash" || type == "hash-quick") 152 { 153 bool quick = (type == "hash-quick"); 154 std::string replayHash; 155 *m_ReplayStream >> replayHash; 156 replayTurnMgr->StoreReplayHash(currentTurn, replayHash, quick); 157 } 158 else if (type == "end") 159 { 160 currentTurn++; 161 } 162 else 163 { 164 CancelLoad(L"Failed to load replay data (unrecognized content)"); 165 } 166 } 167 m_FinalReplayTurn = currentTurn; 168 replayTurnMgr->StoreFinalReplayTurn(currentTurn); 169 return 0; 170 } 171 172 bool CGame::StartReplay(const std::string& replayPath) 173 { 174 m_IsReplay = true; 175 ScriptInterface& scriptInterface = m_Simulation2->GetScriptInterface(); 176 177 SetTurnManager(new CNetReplayTurnManager(*m_Simulation2, GetReplayLogger())); 178 179 m_ReplayPath = replayPath; 180 m_ReplayStream = new std::ifstream(m_ReplayPath.c_str()); 181 182 std::string type; 183 ENSURE((*m_ReplayStream >> type).good() && type == "start"); 184 185 std::string line; 186 std::getline(*m_ReplayStream, line); 187 JS::RootedValue attribs(scriptInterface.GetContext()); 188 scriptInterface.ParseJSON(line, &attribs); 189 StartGame(&attribs, ""); 190 191 return true; 192 } 117 193 118 194 /** 119 195 * Initializes the game with the set of attributes provided. 120 196 * Makes calls to initialize the game view, world, and simulation objects. 121 197 * Calls are made to facilitate progress reporting of the initialization. … … 174 250 RegMemFun(g_Renderer.GetSingletonPtr()->GetWaterManager(), &WaterManager::LoadWaterTextures, L"LoadWaterTextures", 80); 175 251 176 252 if (m_IsSavedGame) 177 253 RegMemFun(this, &CGame::LoadInitialState, L"Loading game", 1000); 178 254 255 if (m_IsReplay) 256 RegMemFun(this, &CGame::LoadReplayData, L"Loading replay data", 1000); 257 179 258 LDR_EndRegistering(); 180 259 } 181 260 182 261 int CGame::LoadInitialState() 183 262 { … … 261 340 int CGame::GetPlayerID() 262 341 { 263 342 return m_PlayerID; 264 343 } 265 344 266 void CGame::SetPlayerID( int playerID)345 void CGame::SetPlayerID(player_id_t playerID) 267 346 { 268 347 m_PlayerID = playerID; 269 348 if (m_TurnManager) 270 349 m_TurnManager->SetPlayerID(m_PlayerID); 271 350 } 272 351 273 352 void CGame::StartGame(JS::MutableHandleValue attribs, const std::string& savedState) 274 353 { 275 m_ReplayLogger->StartGame(attribs); 354 if (m_ReplayLogger != false) 355 m_ReplayLogger->StartGame(attribs); 356 276 357 RegisterInit(attribs, savedState); 277 358 } 278 359 279 360 // TODO: doInterpolate is optional because Atlas interpolates explicitly, 280 361 // so that it has more control over the update rate. The game might want to … … 308 389 { 309 390 { 310 391 PROFILE3("gui sim update"); 311 392 g_GUI->SendEventToAll("SimulationUpdate"); 312 393 } 394 if (m_IsReplay && m_TurnManager->GetCurrentTurn() == m_FinalReplayTurn - 1) 395 g_GUI->SendEventToAll("ReplayFinished"); 313 396 314 397 GetView()->GetLOSTexture().MakeDirty(); 315 398 } 316 399 317 400 if (CRenderer::IsInitialised()) … … 360 443 m_PlayerColours[i] = cmpPlayer->GetColour(); 361 444 } 362 445 } 363 446 364 447 365 CColor CGame::GetPlayerColour( int player) const448 CColor CGame::GetPlayerColour(player_id_t player) const 366 449 { 367 450 if (player < 0 || player >= (int)m_PlayerColours.size()) 368 451 return BrokenColor; 369 452 370 453 return m_PlayerColours[player]; -
source/ps/Game.h
1 /* Copyright (C) 201 3Wildfire Games.1 /* Copyright (C) 2015 Wildfire Games. 2 2 * This file is part of 0 A.D. 3 3 * 4 4 * 0 A.D. is free software: you can redistribute it and/or modify 5 5 * it under the terms of the GNU General Public License as published by 6 6 * the Free Software Foundation, either version 2 of the License, or … … 20 20 21 21 #include "ps/Errors.h" 22 22 #include <vector> 23 23 24 24 #include "scriptinterface/ScriptVal.h" 25 #include "simulation2/helpers/Player.h" 25 26 26 27 class CWorld; 27 28 class CSimulation2; 28 29 class CGameView; 29 30 class CNetTurnManager; … … 58 59 /** 59 60 * Timescale multiplier for simulation rate. 60 61 **/ 61 62 float m_SimRate; 62 63 63 int m_PlayerID;64 player_id_t m_PlayerID; 64 65 65 66 CNetTurnManager* m_TurnManager; 66 67 67 68 public: 68 CGame(bool disableGraphics = false );69 CGame(bool disableGraphics = false, bool replayLog = true); 69 70 ~CGame(); 70 71 71 72 /** 72 73 * the game is paused and no updates will be performed if true. 73 74 **/ 74 75 bool m_Paused; 75 76 76 77 void StartGame(JS::MutableHandleValue attribs, const std::string& savedState); 77 78 PSRETURN ReallyStartGame(); 78 79 80 bool StartReplay(const std::string& replayPath); 81 79 82 /** 80 83 * Periodic heartbeat that controls the process. performs all per-frame updates. 81 84 * Simulation update is called and game status update is called. 82 85 * 83 86 * @param deltaRealTime Elapsed real time since last beat/frame, in seconds. … … 88 91 bool Update(const double deltaRealTime, bool doInterpolate = true); 89 92 90 93 void Interpolate(float simFrameLength, float realFrameLength); 91 94 92 95 int GetPlayerID(); 93 void SetPlayerID( int playerID);96 void SetPlayerID(player_id_t playerID); 94 97 95 98 /** 96 99 * Retrieving player colours from scripts is slow, so this updates an 97 100 * internal cache of all players' colours. 98 101 * Call this just before rendering, so it will always have the latest 99 102 * colours. 100 103 */ 101 104 void CachePlayerColours(); 102 105 103 CColor GetPlayerColour( int player) const;106 CColor GetPlayerColour(player_id_t player) const; 104 107 105 108 /** 106 109 * Get m_GameStarted. 107 110 * 108 111 * @return bool the value of m_GameStarted. … … 169 172 std::vector<CColor> m_PlayerColours; 170 173 171 174 int LoadInitialState(); 172 175 std::string m_InitialSavedState; // valid between RegisterInit and LoadInitialState 173 176 bool m_IsSavedGame; // true if loading a saved game; false for a new game 177 178 int LoadReplayData(); 179 std::string m_ReplayPath; 180 bool m_IsReplay; 181 std::istream* m_ReplayStream; 182 u32 m_FinalReplayTurn; 174 183 }; 175 184 176 185 extern CGame *g_Game; 177 186 178 187 #endif -
source/ps/GameSetup/GameSetup.cpp
880 880 srand(time(NULL)); // NOTE: this rand should *not* be used for simulation! 881 881 } 882 882 883 883 bool Autostart(const CmdLineArgs& args); 884 884 885 // Returns true if and only if the user has intended to replay a file 886 bool VisualReplay(const std::string replayFile); 887 885 888 bool Init(const CmdLineArgs& args, int flags) 886 889 { 887 890 h_mgr_init(); 888 891 889 892 // Do this as soon as possible, because it chdirs … … 1072 1075 1073 1076 ogl_WarnIfError(); 1074 1077 1075 1078 try 1076 1079 { 1077 if (! Autostart(args))1080 if (!VisualReplay(args.Get("replay-visual")) && !Autostart(args)) 1078 1081 { 1079 1082 const bool setup_gui = ((flags & INIT_NO_GUI) == 0); 1080 1083 // We only want to display the splash screen at startup 1081 1084 shared_ptr<ScriptInterface> scriptInterface = g_GUI->GetScriptInterface(); 1082 1085 JSContext* cx = scriptInterface->GetContext(); … … 1472 1475 } 1473 1476 1474 1477 return true; 1475 1478 } 1476 1479 1480 bool VisualReplay(const std::string replayFile) 1481 { 1482 if (!FileExists(OsPath(replayFile))) 1483 return false; 1484 1485 g_Game = new CGame(false, false); 1486 g_Game->SetPlayerID(-1); 1487 g_Game->StartReplay(replayFile); 1488 1489 // TODO: Non progressive load can fail - need a decent way to handle this 1490 LDR_NonprogressiveLoad(); 1491 1492 PSRETURN ret = g_Game->ReallyStartGame(); 1493 ENSURE(ret == PSRETURN_OK); 1494 1495 ScriptInterface& scriptInterface = g_Game->GetSimulation2()->GetScriptInterface(); 1496 1497 InitPs(true, L"page_session.xml", &scriptInterface, JS::UndefinedHandleValue); 1498 return true; 1499 } 1500 1477 1501 void CancelLoad(const CStrW& message) 1478 1502 { 1479 1503 shared_ptr<ScriptInterface> pScriptInterface = g_GUI->GetActiveGUI()->GetScriptInterface(); 1480 1504 JSContext* cx = pScriptInterface->GetContext(); 1481 1505 JSAutoRequest rq(cx); -
source/ps/Replay.cpp
1 /* Copyright (C) 201 4Wildfire Games.1 /* Copyright (C) 2015 Wildfire Games. 2 2 * This file is part of 0 A.D. 3 3 * 4 4 * 0 A.D. is free software: you can redistribute it and/or modify 5 5 * it under the terms of the GNU General Public License as published by 6 6 * the Free Software Foundation, either version 2 of the License, or … … 31 31 #include "scriptinterface/ScriptInterface.h" 32 32 #include "scriptinterface/ScriptStats.h" 33 33 #include "simulation2/Simulation2.h" 34 34 #include "simulation2/helpers/SimulationCommand.h" 35 35 36 #include <ctime> 36 37 #include <sstream> 37 38 #include <fstream> 38 39 #include <iomanip> 39 40 40 #if MSC_VERSION41 #include <process.h>42 #define getpid _getpid // use the non-deprecated function name43 #endif44 45 41 static std::string Hexify(const std::string& s) 46 42 { 47 43 std::stringstream str; 48 44 str << std::hex; 49 45 for (size_t i = 0; i < s.size(); ++i) … … 52 48 } 53 49 54 50 CReplayLogger::CReplayLogger(ScriptInterface& scriptInterface) : 55 51 m_ScriptInterface(scriptInterface) 56 52 { 57 // Construct the directory name based on the PID, to be relatively unique. 58 // Append "-1", "-2" etc if we run multiple matches in a single session, 59 // to avoid accidentally overwriting earlier logs. 60 61 std::wstringstream name; 62 name << getpid(); 63 64 static int run = -1; 65 if (++run) 66 name << "-" << run; 53 // Get current date 54 time_t t = time(NULL); 55 struct tm* now = localtime(&t); 56 char date[16]; 57 sprintf_s(date, ARRAY_SIZE(date), "%04d-%02d-%02d_a18_", 1900+now->tm_year, 1+now->tm_mon, now->tm_mday); 58 59 // Construct a unique directory name based on date and sequential number per date 60 int i = 1; 61 OsPath path; 62 std::stringstream pathname; 63 do 64 { 65 pathname.str(""); 66 pathname.clear(); 67 pathname << date << i++; 68 path = psLogDir() / L"sim_log" / pathname.str() / L"commands.txt"; 69 } while(DirectoryExists(path.Parent())); 67 70 68 OsPath path = psLogDir() / L"sim_log" / name.str() / L"commands.txt";71 // Create directory and commands.txt 69 72 CreateDirectories(path.Parent(), 0700); 70 73 m_Stream = new std::ofstream(OsString(path).c_str(), std::ofstream::out | std::ofstream::trunc); 71 74 } 72 75 73 76 CReplayLogger::~CReplayLogger() … … 120 123 121 124 m_Stream = new std::ifstream(path.c_str()); 122 125 ENSURE(m_Stream->good()); 123 126 } 124 127 125 void CReplayPlayer::Replay(bool serializationtest )128 void CReplayPlayer::Replay(bool serializationtest, bool ooslog) 126 129 { 127 130 ENSURE(m_Stream); 128 131 129 132 new CProfileViewer; 130 133 new CProfileManager; … … 137 140 138 141 CGame game(true); 139 142 g_Game = &game; 140 143 if (serializationtest) 141 144 game.GetSimulation2()->EnableSerializationTest(); 145 if (ooslog) 146 game.GetSimulation2()->EnableOOSLog(); 142 147 143 148 JSContext* cx = game.GetSimulation2()->GetScriptInterface().GetContext(); 144 149 JSAutoRequest rq(cx); 145 150 146 151 // Need some stuff for terrain movement costs: -
source/ps/Replay.h
1 /* Copyright (C) 201 4Wildfire Games.1 /* Copyright (C) 2015 Wildfire Games. 2 2 * This file is part of 0 A.D. 3 3 * 4 4 * 0 A.D. is free software: you can redistribute it and/or modify 5 5 * it under the terms of the GNU General Public License as published by 6 6 * the Free Software Foundation, either version 2 of the License, or … … 54 54 */ 55 55 class CDummyReplayLogger : public IReplayLogger 56 56 { 57 57 public: 58 58 virtual void StartGame(JS::MutableHandleValue UNUSED(attribs)) { } 59 virtual void Turn(u32 UNUSED(n), u32 UNUSED(turnLength), conststd::vector<SimulationCommand>& UNUSED(commands)) { }59 virtual void Turn(u32 UNUSED(n), u32 UNUSED(turnLength), std::vector<SimulationCommand>& UNUSED(commands)) { } 60 60 virtual void Hash(const std::string& UNUSED(hash), bool UNUSED(quick)) { } 61 61 }; 62 62 63 63 /** 64 64 * Implementation of IReplayLogger that saves data to a file in the logs directory. … … 87 87 public: 88 88 CReplayPlayer(); 89 89 ~CReplayPlayer(); 90 90 91 91 void Load(const std::string& path); 92 void Replay(bool serializationtest );92 void Replay(bool serializationtest, bool ooslog); 93 93 94 94 private: 95 95 std::istream* m_Stream; 96 96 }; 97 97 -
source/simulation2/components/CCmpRangeManager.cpp
903 903 } 904 904 905 905 return entities; 906 906 } 907 907 908 virtual std::vector<entity_id_t> GetAllPlayerEntities() 909 { 910 // Compute ownership mask for all players 911 u32 ownerMask = 0; 912 for (player_id_t player = 1; player < MAX_LOS_PLAYER_ID + 1; ++player) 913 ownerMask |= CalcOwnerMask(player); 914 915 // Get a list of all entities of all players 916 std::vector<entity_id_t> entities; 917 for (EntityMap<EntityData>::const_iterator it = m_EntityData.begin(); it != m_EntityData.end(); ++it) 918 { 919 // Check owner and add to list if it matches 920 if (CalcOwnerMask(it->second.owner) & ownerMask) 921 entities.push_back(it->first); 922 } 923 924 return entities; 925 } 926 908 927 virtual void SetDebugOverlay(bool enabled) 909 928 { 910 929 m_DebugOverlayEnabled = enabled; 911 930 m_DebugOverlayDirty = true; 912 931 if (!enabled) -
source/simulation2/components/ICmpRangeManager.cpp
43 43 DEFINE_INTERFACE_METHOD_1("DisableActiveQuery", void, ICmpRangeManager, DisableActiveQuery, ICmpRangeManager::tag_t) 44 44 DEFINE_INTERFACE_METHOD_1("ResetActiveQuery", std::vector<entity_id_t>, ICmpRangeManager, ResetActiveQuery, ICmpRangeManager::tag_t) 45 45 DEFINE_INTERFACE_METHOD_3("SetEntityFlag", void, ICmpRangeManager, SetEntityFlag, entity_id_t, std::string, bool) 46 46 DEFINE_INTERFACE_METHOD_1("GetEntityFlagMask", u8, ICmpRangeManager, GetEntityFlagMask, std::string) 47 47 DEFINE_INTERFACE_METHOD_1("GetEntitiesByPlayer", std::vector<entity_id_t>, ICmpRangeManager, GetEntitiesByPlayer, player_id_t) 48 DEFINE_INTERFACE_METHOD_0("GetAllPlayerEntities", std::vector<entity_id_t>, ICmpRangeManager, GetAllPlayerEntities) 48 49 DEFINE_INTERFACE_METHOD_1("SetDebugOverlay", void, ICmpRangeManager, SetDebugOverlay, bool) 49 50 DEFINE_INTERFACE_METHOD_1("ExploreAllTiles", void, ICmpRangeManager, ExploreAllTiles, player_id_t) 50 51 DEFINE_INTERFACE_METHOD_0("ExploreTerritories", void, ICmpRangeManager, ExploreTerritories) 51 52 DEFINE_INTERFACE_METHOD_2("SetLosRevealAll", void, ICmpRangeManager, SetLosRevealAll, player_id_t, bool) 52 53 DEFINE_INTERFACE_METHOD_1("GetLosRevealAll", bool, ICmpRangeManager, GetLosRevealAll, player_id_t) -
source/simulation2/components/ICmpRangeManager.h
177 177 * @return list of entities matching the query, ordered by increasing distance from the source entity. 178 178 */ 179 179 virtual std::vector<entity_id_t> ResetActiveQuery(tag_t tag) = 0; 180 180 181 181 /** 182 * Returns list of all entities for specific player.182 * Returns a list of all entities for specific player. 183 183 * (This is on this interface because it shares a lot of the implementation. 184 184 * Maybe it should be extended to be more like ExecuteQuery without 185 185 * the range parameter.) 186 186 */ 187 187 virtual std::vector<entity_id_t> GetEntitiesByPlayer(player_id_t player) = 0; 188 188 189 189 /** 190 * Returns a list of all entities for all players. 191 */ 192 virtual std::vector<entity_id_t> GetAllPlayerEntities() = 0; 193 194 /** 190 195 * Toggle the rendering of debug info. 191 196 */ 192 197 virtual void SetDebugOverlay(bool enabled) = 0; 193 198 194 199 /** -
source/simulation2/helpers/Selection.cpp
162 162 } 163 163 164 164 return hitEnts; 165 165 } 166 166 167 std::vector<entity_id_t> EntitySelection::PickAllPlayerEntitiesInRect(CSimulation2& simulation, const CCamera& camera, int sx0, int sy0, int sx1, int sy1, bool allowEditorSelectables) 168 { 169 PROFILE2("PickAllPlayerEntitiesInRect"); 170 // Make sure sx0 <= sx1, and sy0 <= sy1 171 if (sx0 > sx1) 172 std::swap(sx0, sx1); 173 if (sy0 > sy1) 174 std::swap(sy0, sy1); 175 176 CmpPtr<ICmpRangeManager> cmpRangeManager(simulation, SYSTEM_ENTITY); 177 ENSURE(cmpRangeManager); 178 179 std::vector<entity_id_t> hitEnts; 180 181 CComponentManager& componentManager = simulation.GetSimContext().GetComponentManager(); 182 183 std::vector<entity_id_t> ents = cmpRangeManager->GetAllPlayerEntities(); 184 for (std::vector<entity_id_t>::iterator it = ents.begin(); it != ents.end(); ++it) 185 { 186 CmpPtr<ICmpOwnership> cmpOwnership(simulation.GetSimContext(), *it); 187 if (CheckEntityVisibleAndInRect(componentManager.LookupEntityHandle(*it), cmpRangeManager, camera, sx0, sy0, sx1, sy1, cmpOwnership->GetOwner(), allowEditorSelectables)) 188 hitEnts.push_back(*it); 189 } 190 191 return hitEnts; 192 } 193 167 194 std::vector<entity_id_t> EntitySelection::PickSimilarEntities(CSimulation2& simulation, const CCamera& camera, 168 195 const std::string& templateName, player_id_t owner, bool includeOffScreen, bool matchRank, 169 196 bool allowEditorSelectables, bool allowFoundations) 170 197 { 171 198 PROFILE2("PickSimilarEntities"); -
source/simulation2/helpers/Selection.h
63 63 * @return unordered list of selected entities. 64 64 */ 65 65 std::vector<entity_id_t> PickEntitiesInRect(CSimulation2& simulation, const CCamera& camera, int sx0, int sy0, int sx1, int sy1, player_id_t owner, bool allowEditorSelectables); 66 66 67 67 /** 68 * Finds all selectable entities within the given screen coordinate rectangle, 69 * belonging to any player. 70 * 71 * @param camera use this view to convert screen to world coordinates. 72 * @param sx0,sy0,sx1,sy1 diagonally opposite corners of the rectangle in 2D screen coordinates. 73 * @param owner player whose entities we are selecting. Ownership is ignored if 74 * INVALID_PLAYER is used. 75 * @param allowEditorSelectables if true, all entities with the ICmpSelectable interface 76 * will be selected (including decorative actors), else only those selectable ingame. 77 * 78 * @return unordered list of selected entities. 79 */ 80 std::vector<entity_id_t> PickAllPlayerEntitiesInRect(CSimulation2& simulation, const CCamera& camera, int sx0, int sy0, int sx1, int sy1, bool allowEditorSelectables); 81 82 /** 68 83 * Finds all entities with the given entity template name, belonging to the given player. 69 84 * 70 85 * @param camera use this view to convert screen to world coordinates. 71 86 * @param templateName the name of the template to match, or the selection group name 72 87 * for similar matching.