Ticket #785: gamespeed-03152013.patch
File gamespeed-03152013.patch, 38.4 KB (added by , 11 years ago) |
---|
-
binaries/data/mods/public/gui/common/functions_utility.js
134 134 135 135 // ==================================================================== 136 136 137 // Load default player data, for when it's not otherwise specified 138 function initPlayerDefaults() 137 // Parse and return JSON data from file in simulation/data/* 138 // returns valid object or undefined on error 139 function parseJSONFromDataFile(filename) 139 140 { 140 var filename = "simulation/data/player_defaults.json"; 141 var defaults = []; 142 var rawData = readFile(filename); 141 var path = "simulation/data/"+filename; 142 var rawData = readFile(path); 143 143 if (!rawData) 144 error("Failed to read player defaults file: "+filename);145 144 error("Failed to read file: "+path); 145 146 146 try 147 { // Catch nasty errors from JSON parsing 147 { 148 // Catch nasty errors from JSON parsing 148 149 // TODO: Need more info from the parser on why it failed: line number, position, etc! 149 150 var data = JSON.parse(rawData); 150 if (!data || !data.PlayerData) 151 error("Failed to parse player defaults in: "+filename+" (check for valid JSON data)"); 152 153 defaults = data.PlayerData; 151 return data; 154 152 } 155 153 catch(err) 156 154 { 157 error(err.toString()+": parsing player defaults in "+filename);155 error(err.toString()+": parsing JSON data in "+path); 158 156 } 159 157 158 return undefined; 159 } 160 161 // ==================================================================== 162 163 // Load default player data, for when it's not otherwise specified 164 function initPlayerDefaults() 165 { 166 var defaults = []; 167 168 var data = parseJSONFromDataFile("player_defaults.json"); 169 if (!data || !data.PlayerData) 170 error("Failed to parse player defaults in player_defaults.json (check for valid JSON data)"); 171 else 172 defaults = data.PlayerData; 173 160 174 return defaults; 161 175 } 162 176 … … 165 179 // Load map size data 166 180 function initMapSizes() 167 181 { 168 var filename = "simulation/data/map_sizes.json";169 182 var sizes = { 170 names:[],171 tiles: [],172 default: 0183 "names":[], 184 "tiles": [], 185 "default": 0 173 186 }; 174 var rawData = readFile(filename); 175 if (!rawData) 176 error("Failed to read map sizes file: "+filename); 177 178 try 179 { // Catch nasty errors from JSON parsing 180 // TODO: Need more info from the parser on why it failed: line number, position, etc! 181 var data = JSON.parse(rawData); 182 if (!data || !data.Sizes) 183 error("Failed to parse map sizes in: "+filename+" (check for valid JSON data)"); 184 187 188 var data = parseJSONFromDataFile("map_sizes.json"); 189 if (!data || !data.Sizes) 190 error("Failed to parse map sizes in map_sizes.json (check for valid JSON data)"); 191 else 192 { 185 193 for (var i = 0; i < data.Sizes.length; ++i) 186 194 { 187 195 sizes.names.push(data.Sizes[i].LongName); 188 196 sizes.tiles.push(data.Sizes[i].Tiles); 189 197 190 198 if (data.Sizes[i].Default) 191 sizes .default= i;199 sizes["default"] = i; 192 200 } 193 201 } 194 catch(err) 202 203 return sizes; 204 } 205 206 // ==================================================================== 207 208 // Load game speed data 209 function initGameSpeeds() 210 { 211 var gameSpeeds = { 212 "names": [], 213 "speeds": [], 214 "default": 0 215 }; 216 217 var data = parseJSONFromDataFile("game_speeds.json"); 218 if (!data || !data.Speeds) 219 error("Failed to parse game speeds in game_speeds.json (check for valid JSON data)"); 220 else 195 221 { 196 error(err.toString()+": parsing map sizes in "+filename); 222 for (var i = 0; i < data.Speeds.length; ++i) 223 { 224 gameSpeeds.names.push(data.Speeds[i].Name); 225 gameSpeeds.speeds.push(data.Speeds[i].Speed); 226 227 if (data.Speeds[i].Default) 228 gameSpeeds["default"] = i; 229 } 197 230 } 198 199 return sizes;231 232 return gameSpeeds; 200 233 } 201 234 235 202 236 // ==================================================================== 203 237 204 238 // Convert integer color values to string (for use in GUI objects) -
binaries/data/mods/public/gui/gamesetup/gamesetup.js
36 36 settings: {} 37 37 }; 38 38 39 var g_GameSpeeds = {}; 39 40 var g_MapSizes = {}; 40 41 41 42 var g_AIs = []; … … 97 98 g_DefaultPlayerData.shift(); 98 99 for (var i = 0; i < g_DefaultPlayerData.length; i++) 99 100 g_DefaultPlayerData[i].Civ = "random"; 100 101 102 g_GameSpeeds = initGameSpeeds(); 101 103 g_MapSizes = initMapSizes(); 102 104 103 105 // Init civs … … 135 137 numPlayersSelection.list = players; 136 138 numPlayersSelection.list_data = players; 137 139 numPlayersSelection.selected = MAX_PLAYERS - 1; 140 141 var gameSpeed = getGUIObjectByName("gameSpeed"); 142 gameSpeed.hidden = false; 143 getGUIObjectByName("gameSpeedText").hidden = true; 144 gameSpeed.list = g_GameSpeeds.names; 145 gameSpeed.list_data = g_GameSpeeds.speeds; 146 gameSpeed.onSelectionChange = function() 147 { 148 // Update attributes so other players can see change 149 if (this.selected != -1) 150 { 151 g_GameAttributes.gameSpeed = g_GameSpeeds.speeds[this.selected]; 152 g_GameAttributes.gameSpeedName = g_GameSpeeds.names[this.selected]; 153 } 138 154 155 if (!g_IsInGuiUpdate) 156 updateGameAttributes(); 157 } 158 gameSpeed.selected = g_GameSpeeds["default"]; 159 160 var lockGameSpeed = getGUIObjectByName("lockGameSpeed"); 161 lockGameSpeed.hidden = false; 162 getGUIObjectByName("lockGameSpeedText").hidden = true; 163 lockGameSpeed.onPress = function() 164 { 165 // Update attributes so other players can see change 166 g_GameAttributes.lockGameSpeed = this.checked; 167 168 if (!g_IsInGuiUpdate) 169 updateGameAttributes(); 170 } 171 139 172 var populationCaps = getGUIObjectByName("populationCap"); 140 173 populationCaps.list = POPULATION_CAP; 141 174 populationCaps.list_data = POPULATION_CAP_DATA; … … 245 278 getGUIObjectByName("mapSelection").hidden = true; 246 279 getGUIObjectByName("victoryConditionText").hidden = false; 247 280 getGUIObjectByName("victoryCondition").hidden = true; 248 281 getGUIObjectByName("gameSpeedText").hidden = false; 282 getGUIObjectByName("gameSpeed").hidden = true; 283 getGUIObjectByName("lockGameSpeedText").hidden = false; 284 getGUIObjectByName("lockGameSpeed").hidden = true; 285 249 286 // Disable player and game options controls 250 287 // TODO: Shouldn't players be able to choose their own assignment? 251 288 for (var i = 0; i < MAX_PLAYERS; ++i) … … 934 971 populationCapBox.selected = populationCapBox.list_data.indexOf(mapSettings.PopulationCap); 935 972 var startingResourcesBox = getGUIObjectByName("startingResources"); 936 973 startingResourcesBox.selected = startingResourcesBox.list_data.indexOf(mapSettings.StartingResources); 974 getGUIObjectByName("gameSpeedText").caption = g_GameAttributes.gameSpeedName; 975 getGUIObjectByName("lockGameSpeedText").caption = g_GameAttributes.lockGameSpeed ? "Yes" : "No"; 937 976 initMapNameList(); 938 977 } 939 978 … … 956 995 var populationCapText = getGUIObjectByName("populationCapText"); 957 996 var startingResourcesText = getGUIObjectByName("startingResourcesText"); 958 997 959 var sizeIdx = (g_MapSizes.tiles.indexOf(mapSettings.Size) != -1 ? g_MapSizes.tiles.indexOf(mapSettings.Size) : g_MapSizes .default);998 var sizeIdx = (g_MapSizes.tiles.indexOf(mapSettings.Size) != -1 ? g_MapSizes.tiles.indexOf(mapSettings.Size) : g_MapSizes["default"]); 960 999 var victoryIdx = (VICTORY_DATA.indexOf(mapSettings.GameType) != -1 ? VICTORY_DATA.indexOf(mapSettings.GameType) : VICTORY_DEFAULTIDX); 961 1000 enableCheats.checked = (g_GameAttributes.settings.CheatsEnabled === undefined || !g_GameAttributes.settings.CheatsEnabled ? false : true) 962 1001 enableCheatsText.caption = (enableCheats.checked ? "Yes" : "No"); -
binaries/data/mods/public/gui/gamesetup/gamesetup.xml
178 178 </object> 179 179 180 180 <!-- More Options --> 181 <object name="moreOptions" type="image" sprite="StoneWindow" size="50%-200 50%-1 20 50%+200 50%+155" z="70" hidden="true">181 <object name="moreOptions" type="image" sprite="StoneWindow" size="50%-200 50%-164 50%+200 50%+169" z="70" hidden="true"> 182 182 <object style="TitleText" type="text" size="50%-128 11 50%+128 27"> 183 183 More Options 184 184 </object> 185 185 186 186 <object size="14 38 94% 66"> 187 187 <object size="0 0 40% 28"> 188 <object size="0 0 100% 100%" type="text" style="RightLabelText"> Victory condition:</object>188 <object size="0 0 100% 100%" type="text" style="RightLabelText">Game Speed:</object> 189 189 </object> 190 <object name="gameSpeedText" size="40% 0 100% 100%" type="text" style="LeftLabelText"/> 191 <object name="gameSpeed" size="40% 0 100% 28" type="dropdown" style="StoneDropDown" hidden="true" tooltip_style="onscreenToolTip" tooltip="Select game speed."/> 192 </object> 193 194 <object size="14 68 94% 96"> 195 <object size="0 0 40% 28"> 196 <object size="0 0 100% 100%" type="text" style="RightLabelText">Lock Game Speed:</object> 197 </object> 198 <object name="lockGameSpeedText" size="40% 0 100% 100%" type="text" style="LeftLabelText"/> 199 <object name="lockGameSpeed" size="40%+4 50%-8 40%+20 50%+8" type="checkbox" style="StoneCrossBox" hidden="true" tooltip_style="onscreenToolTip" tooltip="Toggle locked game speed."/> 200 </object> 201 202 <object size="14 98 94% 126"> 203 <object size="0 0 40% 28"> 204 <object size="0 0 100% 100%" type="text" style="RightLabelText">Victory Condition:</object> 205 </object> 190 206 <object name="victoryConditionText" size="40% 0 100% 100%" type="text" style="LeftLabelText"/> 191 207 <object name="victoryCondition" size="40% 0 100% 28" type="dropdown" style="StoneDropDown" hidden="true" tooltip_style="onscreenToolTip" tooltip="Select victory condition."/> 192 208 </object> 193 209 194 <object size="14 68 94% 96">210 <object size="14 128 94% 156"> 195 211 <object size="0 0 40% 28"> 196 212 <object size="0 0 100% 100%" type="text" style="RightLabelText">Population Cap:</object> 197 213 </object> … … 199 215 <object name="populationCap" size="40% 0 100% 28" type="dropdown" style="StoneDropDown" hidden="true" tooltip_style="onscreenToolTip" tooltip="Select population cap."/> 200 216 </object> 201 217 202 <object size="14 98 94% 126">218 <object size="14 158 94% 186"> 203 219 <object size="0 0 40% 28"> 204 220 <object size="0 0 100% 100%" type="text" style="RightLabelText">Starting Resources:</object> 205 221 </object> … … 207 223 <object name="startingResources" size="40% 0 100% 28" type="dropdown" style="StoneDropDown" hidden="true" tooltip_style="onscreenToolTip" tooltip="Select the game's starting resources."/> 208 224 </object> 209 225 210 <object size="14 1 28 94% 216">226 <object size="14 188 94% 276"> 211 227 <object size="0 0 40% 28"> 212 <object size="0 0 100% 100%" type="text" style="RightLabelText">Reveal map:</object>228 <object size="0 0 100% 100%" type="text" style="RightLabelText">Reveal Map:</object> 213 229 </object> 214 230 <object size="0 30 40% 58"> 215 <object size="0 0 100% 100%" type="text" style="RightLabelText">Teams locked:</object>231 <object size="0 0 100% 100%" type="text" style="RightLabelText">Teams Locked:</object> 216 232 </object> 217 233 <object size="0 60 40% 88" name="enableCheatsDesc" hidden="true"> 218 234 <object size="0 0 100% 100%" type="text" style="RightLabelText">Cheats:</object> … … 237 253 name="hideMoreOptions" 238 254 type="button" 239 255 style="StoneButton" 240 size="50%-70 2 18 50%+70 246"256 size="50%-70 278 50%+70 304" 241 257 tooltip_style="onscreenToolTip" 242 258 tooltip="Close more game options window" 243 259 > -
binaries/data/mods/public/gui/session/menu.js
361 361 openDiplomacy(); 362 362 }; 363 363 364 function toggleGameSpeed() 365 { 366 var gameSpeed = getGUIObjectByName("gameSpeed"); 367 gameSpeed.hidden = !gameSpeed.hidden; 368 } 369 364 370 function pauseGame() 365 371 { 366 372 getGUIObjectByName("pauseButtonText").caption = RESUME; -
binaries/data/mods/public/gui/session/messages.js
186 186 case "chat": 187 187 addChatMessage({ "type": "message", "guid": message.guid, "text": message.text }); 188 188 break; 189 190 case "requestspeed": 191 addChatMessage({ "type": "requestspeed", "guid": message.guid, "speed": message.speed }); 192 break; 189 193 194 case "speedchanged": 195 g_CurrentSpeed = message.speed; 196 addChatMessage({ "type": "speedchanged", "speed": message.speed }); 197 break; 198 190 199 // To prevent errors, ignore these message types that occur during autostart 191 200 case "gamesetup": 192 201 case "start": … … 352 361 353 362 formatted = "[color=\"" + playerColor + "\"]" + username + "[/color] has sent you " + amounts + "."; 354 363 break; 364 case "requestspeed": 365 formatted = "[color=\"" + playerColor + "\"]" + username + "[/color] voted to change game speed to " + msg.speed/100.0 + "x."; 366 break; 367 case "speedchanged": 368 formatted = "Game speed has been changed to " + msg.speed/100.0 + "x."; 369 break; 355 370 case "message": 356 371 // May have been hidden by the 'team' command. 357 372 if (msg.hide) -
binaries/data/mods/public/gui/session/session.js
6 6 // Cache the useful civ data 7 7 var g_CivData = {}; 8 8 9 var g_GameSpeeds = {}; 10 var g_CurrentSpeed; 11 9 12 var g_PlayerAssignments = { "local": { "name": "You", "player": 1 } }; 10 13 11 14 // Cache dev-mode settings that are frequently or widely used … … 83 86 g_Players = getPlayerData(g_PlayerAssignments); 84 87 85 88 if (initData.savedGUIData) 86 {87 89 restoreSavedGameData(initData.savedGUIData); 88 } 90 91 getGUIObjectByName("gameSpeedButton").hidden = initData.attribs && initData.attribs.lockGameSpeed; 89 92 } 90 93 else // Needed for autostart loading option 91 94 { … … 96 99 g_CivData = loadCivData(); 97 100 g_CivData["gaia"] = { "Code": "gaia", "Name": "Gaia" }; 98 101 102 g_GameSpeeds = initGameSpeeds(); 103 g_CurrentSpeed = Engine.GetGameSpeed(); 104 warn("current speed: "+g_CurrentSpeed); 105 var gameSpeed = getGUIObjectByName("gameSpeed"); 106 gameSpeed.list = g_GameSpeeds.names; 107 gameSpeed.list_data = g_GameSpeeds.speeds; 108 var idx = -1; 109 for (var s in g_GameSpeeds.speeds) 110 { 111 if (g_GameSpeeds.speeds[s] == g_CurrentSpeed) 112 { 113 idx = s; 114 break; 115 } 116 } 117 gameSpeed.selected = idx != -1 ? idx : g_GameSpeeds["default"]; 118 gameSpeed.onSelectionChange = function() { changeGameSpeed(+this.list_data[this.selected]); } 119 99 120 getGUIObjectByName("civIcon").sprite = "stretched:" + g_CivData[g_Players[Engine.GetPlayerID()].civ].Emblem; 100 121 getGUIObjectByName("civIcon").tooltip = g_CivData[g_Players[Engine.GetPlayerID()].civ].Name; 101 122 initMenuPosition(); // set initial position … … 347 368 } 348 369 } 349 370 371 function changeGameSpeed(speed) 372 { 373 // For networked games, we must request a speed change in a sync'd way 374 if (g_IsNetworked) 375 Engine.RequestNetworkGameSpeed(speed); 376 else 377 { 378 Engine.SetSimRate(speed/100.0); 379 g_CurrentSpeed = Engine.GetGameSpeed(); 380 } 381 } 382 350 383 /** 351 384 * Recomputes GUI state that depends on simulation state or selection state. Called directly every simulation 352 385 * update (see session.xml), or from onTick when the selection has changed. … … 521 554 function updateTimeElapsedCounter(simState) 522 555 { 523 556 var timeElapsedCounter = getGUIObjectByName("timeElapsedCounter"); 524 timeElapsedCounter.caption = timeToString(simState.timeElapsed) ;557 timeElapsedCounter.caption = timeToString(simState.timeElapsed) + " (" + g_CurrentSpeed/100.0 + "x)"; 525 558 } 526 559 527 560 // Toggles the display of status bars for all of the player's entities. -
binaries/data/mods/public/gui/session/session.xml
220 220 <!-- ================================ ================================ --> 221 221 <!-- Time elapsed counter --> 222 222 <!-- ================================ ================================ --> 223 <object size="100%-100 50 100%-10 70" type="text" name="timeElapsedCounter" style="SettingsText" hotkey="timeelapsedcounter.toggle" hidden="true"> 223 224 <object size="100%-120 45 100%-10 65" type="text" name="timeElapsedCounter" style="SettingsText" hotkey="timeelapsedcounter.toggle" hidden="true"> 224 225 <action on="Press">this.hidden = !this.hidden;</action> 225 226 </object> 226 227 … … 513 514 <!-- ================================ ================================ --> 514 515 515 516 <!-- Displays Alpha name and number --> 516 <object size=" 70%-128 0 70%+128100%" name="alphaLabel" type="text" style="CenteredLabelText" text_valign="top" ghost="true">517 <object size="50%+48 0 100%-226 100%" name="alphaLabel" type="text" style="CenteredLabelText" text_valign="top" ghost="true"> 517 518 ALPHA XIII : Magadha<!-- IMPORTANT: remember to update pregame/mainmenu.xml in sync with this --> 518 519 519 520 <!-- Displays build date and revision number--> … … 522 523 </object> 523 524 </object> 524 525 526 <!-- ================================ ================================ --> 527 <!-- Game Speed Button --> 525 528 <!-- ================================ ================================ --> 529 <object type="button" 530 name="gameSpeedButton" 531 size="100%-226 4 100%-198 32" 532 style="iconButton" 533 tooltip_style="sessionToolTip" 534 tooltip="Game speed" 535 > 536 <object size="5 5 100%-5 100%-5" type="image" sprite="stretched:session/icons/resources/time_small.png" ghost="true"/> 537 <action on="Press"> 538 toggleGameSpeed(); 539 </action> 540 </object> 541 542 <object size="100%-230 45 100%-140 70" name="gameSpeed" type="dropdown" buffer_zone="5" style="StoneDropDown" hidden="true" tooltip="Choose game speed" tooltip_style="sessionToolTip"/> 543 544 <!-- ================================ ================================ --> 526 545 <!-- Diplomacy Button --> 527 546 <!-- ================================ ================================ --> 528 547 <object type="button" 529 548 name="diplomacyButton1" 530 size="100%-19 6 0 100%-16432"549 size="100%-194 4 100%-166 32" 531 550 style="iconButton" 532 551 tooltip_style="sessionToolTip" 533 552 tooltip="Diplomacy" -
source/gui/scripting/ScriptFunctions.cpp
1 /* Copyright (C) 201 2Wildfire Games.1 /* Copyright (C) 2013 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 … … 358 358 g_NetClient->SendChatMessage(message); 359 359 } 360 360 361 /** 362 * Send a request for changing game speed in a networked game. 363 * Requires the network client to have been initialized. 364 * @param speed Requested game speed in percent, 100=normal, 50=half speed, etc. 365 */ 366 void RequestNetworkGameSpeed(void* UNUSED(cbdata), u32 speed) 367 { 368 ENSURE(g_NetClient); 369 370 g_NetClient->RequestGameSpeed(speed); 371 } 372 361 373 std::vector<CScriptValRooted> GetAIs(void* cbdata) 362 374 { 363 375 CGUIManager* guiManager = static_cast<CGUIManager*> (cbdata); … … 520 532 } 521 533 522 534 523 535 /** 536 * Set the simulation rate (game speed) directly. 537 * Only reliably useful in non-networked games. 538 * 539 * @param rate Desired simulation rate, 1.0 is normal, 0.0 is paused 540 */ 524 541 void SetSimRate(void* UNUSED(cbdata), float rate) 525 542 { 526 g_Game->SetSimRate(rate); 543 if (g_Game) 544 g_Game->SetSimRate(rate); 527 545 } 528 546 547 /** 548 * Get the current game speed 549 * @return positive integer game speed if game started, else -1 550 */ 551 int GetGameSpeed(void* UNUSED(cbdata)) 552 { 553 int speed = -1; 554 if (g_Game && g_Game->IsGameStarted()) 555 speed = g_Game->GetGameSpeed(); 556 return speed; 557 } 558 529 559 void SetTurnLength(void* UNUSED(cbdata), int length) 530 560 { 531 561 if (g_NetServer) … … 638 668 scriptInterface.RegisterFunction<void, CScriptVal, &SetNetworkGameAttributes>("SetNetworkGameAttributes"); 639 669 scriptInterface.RegisterFunction<void, int, std::string, &AssignNetworkPlayer>("AssignNetworkPlayer"); 640 670 scriptInterface.RegisterFunction<void, std::wstring, &SendNetworkChat>("SendNetworkChat"); 671 scriptInterface.RegisterFunction<void, u32, &RequestNetworkGameSpeed>("RequestNetworkGameSpeed"); 672 scriptInterface.RegisterFunction<int, &GetGameSpeed>("GetGameSpeed"); 641 673 scriptInterface.RegisterFunction<std::vector<CScriptValRooted>, &GetAIs>("GetAIs"); 642 674 643 675 // Saved games -
source/network/NetClient.cpp
1 /* Copyright (C) 201 1Wildfire Games.1 /* Copyright (C) 2013 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 … … 98 98 AddTransition(NCS_JOIN_SYNCING, (uint)NMT_SIMULATION_COMMAND, NCS_JOIN_SYNCING, (void*)&OnInGame, context); 99 99 AddTransition(NCS_JOIN_SYNCING, (uint)NMT_END_COMMAND_BATCH, NCS_JOIN_SYNCING, (void*)&OnJoinSyncEndCommandBatch, context); 100 100 AddTransition(NCS_JOIN_SYNCING, (uint)NMT_LOADED_GAME, NCS_INGAME, (void*)&OnLoadedGame, context); 101 AddTransition(NCS_JOIN_SYNCING, (uint)NMT_REQUEST_SPEED, NCS_JOIN_SYNCING, (void*)&OnRequestSpeed, context); 102 AddTransition(NCS_JOIN_SYNCING, (uint)NMT_SPEED_CHANGED, NCS_JOIN_SYNCING, (void*)&OnSpeedChanged, context); 101 103 102 104 AddTransition(NCS_LOADING, (uint)NMT_CHAT, NCS_LOADING, (void*)&OnChat, context); 103 105 AddTransition(NCS_LOADING, (uint)NMT_GAME_SETUP, NCS_LOADING, (void*)&OnGameSetup, context); … … 110 112 AddTransition(NCS_INGAME, (uint)NMT_SIMULATION_COMMAND, NCS_INGAME, (void*)&OnInGame, context); 111 113 AddTransition(NCS_INGAME, (uint)NMT_SYNC_ERROR, NCS_INGAME, (void*)&OnInGame, context); 112 114 AddTransition(NCS_INGAME, (uint)NMT_END_COMMAND_BATCH, NCS_INGAME, (void*)&OnInGame, context); 115 AddTransition(NCS_INGAME, (uint)NMT_REQUEST_SPEED, NCS_INGAME, (void*)&OnRequestSpeed, context); 116 AddTransition(NCS_INGAME, (uint)NMT_SPEED_CHANGED, NCS_INGAME, (void*)&OnSpeedChanged, context); 113 117 114 118 // Set first state 115 119 SetFirstState(NCS_UNCONNECTED); … … 247 251 SendMessage(&chat); 248 252 } 249 253 254 void CNetClient::RequestGameSpeed(u32 speed) 255 { 256 CRequestSpeedMessage msg; 257 msg.m_Speed = speed; 258 SendMessage(&msg); 259 } 260 250 261 bool CNetClient::HandleMessage(CNetMessage* message) 251 262 { 252 263 // Handle non-FSM messages first … … 503 514 CEndCommandBatchMessage* endMessage = (CEndCommandBatchMessage*)event->GetParamRef(); 504 515 505 516 client->m_ClientTurnManager->FinishedAllCommands(endMessage->m_Turn, endMessage->m_TurnLength); 517 client->m_Game->SetGameSpeed(endMessage->m_GameSpeed); 506 518 507 519 // Execute all the received commands for the latest turn 508 520 client->m_ClientTurnManager->UpdateFastForward(); … … 550 562 { 551 563 CEndCommandBatchMessage* endMessage = static_cast<CEndCommandBatchMessage*> (message); 552 564 client->m_ClientTurnManager->FinishedAllCommands(endMessage->m_Turn, endMessage->m_TurnLength); 565 client->m_Game->SetGameSpeed(endMessage->m_GameSpeed); 553 566 } 554 567 } 555 568 … … 577 590 578 591 return guid; 579 592 } 593 594 bool CNetClient::OnRequestSpeed(void* context, CFsmEvent* event) 595 { 596 ENSURE(event->GetType() == (uint)NMT_REQUEST_SPEED); 597 598 CNetClient* client = (CNetClient*)context; 599 600 CRequestSpeedMessage* message = (CRequestSpeedMessage*)event->GetParamRef(); 601 602 CScriptValRooted msg; 603 client->GetScriptInterface().Eval("({'type':'requestspeed'})", msg); 604 client->GetScriptInterface().SetProperty(msg.get(), "guid", std::string(message->m_GUID)); 605 client->GetScriptInterface().SetProperty(msg.get(), "speed", message->m_Speed); 606 client->PushGuiMessage(msg); 607 608 return true; 609 } 610 611 bool CNetClient::OnSpeedChanged(void* context, CFsmEvent* event) 612 { 613 ENSURE(event->GetType() == (uint)NMT_SPEED_CHANGED); 614 615 CNetClient* client = (CNetClient*)context; 616 617 CSpeedChangedMessage* message = (CSpeedChangedMessage*)event->GetParamRef(); 618 619 CScriptValRooted msg; 620 client->GetScriptInterface().Eval("({'type':'speedchanged'})", msg); 621 client->GetScriptInterface().SetProperty(msg.get(), "speed", message->m_Speed); 622 client->PushGuiMessage(msg); 623 624 return true; 625 } -
source/network/NetClient.h
1 /* Copyright (C) 201 1Wildfire Games.1 /* Copyright (C) 2013 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 … … 165 165 166 166 void SendChatMessage(const std::wstring& text); 167 167 168 void RequestGameSpeed(u32 speed); 169 168 170 private: 169 171 // Net message / FSM transition handlers 170 172 static bool OnConnect(void* context, CFsmEvent* event); … … 179 181 static bool OnJoinSyncStart(void* context, CFsmEvent* event); 180 182 static bool OnJoinSyncEndCommandBatch(void* context, CFsmEvent* event); 181 183 static bool OnLoadedGame(void* context, CFsmEvent* event); 184 static bool OnRequestSpeed(void* context, CFsmEvent* event); 185 static bool OnSpeedChanged(void* context, CFsmEvent* event); 182 186 183 187 /** 184 188 * Take ownership of a session object, and use it for all network communication. -
source/network/NetMessage.cpp
1 /* Copyright (C) 201 1Wildfire Games.1 /* Copyright (C) 2013 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 … … 179 179 pNewMessage = new CSimulationMessage(scriptInterface); 180 180 break; 181 181 182 case NMT_REQUEST_SPEED: 183 pNewMessage = new CRequestSpeedMessage; 184 break; 185 186 case NMT_SPEED_CHANGED: 187 pNewMessage = new CSpeedChangedMessage; 188 break; 189 182 190 default: 183 191 LOGERROR(L"CNetMessageFactory::CreateMessage(): Unknown message type '%d' received", header.GetType()); 184 192 break; -
source/network/NetMessages.h
1 /* Copyright (C) 201 1Wildfire Games.1 /* Copyright (C) 2013 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 … … 62 62 NMT_SYNC_CHECK, // OOS-detection hash checking 63 63 NMT_SYNC_ERROR, // OOS-detection error 64 64 NMT_SIMULATION_COMMAND, 65 NMT_REQUEST_SPEED, 66 NMT_SPEED_CHANGED, 65 67 NMT_LAST // Last message in the list 66 68 }; 67 69 … … 158 160 START_NMT_CLASS_(EndCommandBatch, NMT_END_COMMAND_BATCH) 159 161 NMT_FIELD_INT(m_Turn, u32, 4) 160 162 NMT_FIELD_INT(m_TurnLength, u32, 2) 163 NMT_FIELD_INT(m_GameSpeed, u32, 2) // ignored when client->server, valid when server->client 161 164 END_NMT_CLASS() 162 165 163 166 START_NMT_CLASS_(SyncCheck, NMT_SYNC_CHECK) … … 170 173 NMT_FIELD(CStr, m_HashExpected) 171 174 END_NMT_CLASS() 172 175 176 START_NMT_CLASS_(RequestSpeed, NMT_REQUEST_SPEED) 177 NMT_FIELD(CStr8, m_GUID) // ignored when client->server, valid when server->client 178 NMT_FIELD_INT(m_Speed, u32, 2) 179 END_NMT_CLASS() 180 181 START_NMT_CLASS_(SpeedChanged, NMT_SPEED_CHANGED) 182 NMT_FIELD_INT(m_Speed, u32, 2) 183 END_NMT_CLASS() 184 173 185 END_NMTS() 174 186 175 187 #else -
source/network/NetServer.cpp
1 /* Copyright (C) 201 1Wildfire Games.1 /* Copyright (C) 2013 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 … … 120 120 121 121 m_ServerName = DEFAULT_SERVER_NAME; 122 122 m_WelcomeMessage = DEFAULT_WELCOME_MESSAGE; 123 124 m_InitialGameSpeed = m_GameSpeed = 100; 125 m_AllowGameSpeedRequests = true; 123 126 } 124 127 125 128 CNetServerWorker::~CNetServerWorker() … … 216 219 return ok; 217 220 } 218 221 222 u32 CNetServerWorker::GetGameSpeed() 223 { 224 return m_GameSpeed; 225 } 226 219 227 void* CNetServerWorker::RunThread(void* data) 220 228 { 221 229 debug_SetThreadName("NetServer"); … … 440 448 session->AddTransition(NSS_INGAME, (uint)NMT_SIMULATION_COMMAND, NSS_INGAME, (void*)&OnInGame, context); 441 449 session->AddTransition(NSS_INGAME, (uint)NMT_SYNC_CHECK, NSS_INGAME, (void*)&OnInGame, context); 442 450 session->AddTransition(NSS_INGAME, (uint)NMT_END_COMMAND_BATCH, NSS_INGAME, (void*)&OnInGame, context); 451 session->AddTransition(NSS_INGAME, (uint)NMT_REQUEST_SPEED, NSS_INGAME, (void*)&OnRequestSpeed, context); 443 452 444 453 // Set first state 445 454 session->SetFirstState(NSS_HANDSHAKE); … … 743 752 return true; 744 753 } 745 754 755 bool CNetServerWorker::OnRequestSpeed(void* context, CFsmEvent* event) 756 { 757 ENSURE(event->GetType() == (uint)NMT_REQUEST_SPEED); 758 759 CNetServerSession* session = (CNetServerSession*)context; 760 CNetServerWorker& server = session->GetServer(); 761 762 CRequestSpeedMessage* requestMessage = (CRequestSpeedMessage*)event->GetParamRef(); 763 764 // TODO: this shouldn't happen but maybe we need a better error? 765 if (!server.m_AllowGameSpeedRequests) 766 return false; 767 768 // Clamp speed to minimum of 25, no upper limit is necessary 769 // (slower speeds mean future messages will be very slowly processed by clients, 770 // so we can't use this to implement pause either) 771 // TODO: restriction can be removed if we sync time between clients 772 u32 desiredSpeed = std::max(requestMessage->m_Speed, (u32)25); 773 session->SetGameSpeed(desiredSpeed); 774 775 // Set GUID so players know who requested this 776 requestMessage->m_GUID = session->GetGUID(); 777 requestMessage->m_Speed = desiredSpeed; 778 server.Broadcast(requestMessage); 779 780 server.UpdateGameSpeed(); 781 782 return true; 783 } 784 785 void CNetServerWorker::UpdateGameSpeed() 786 { 787 // Current "voting" approach: 788 // choose least speed that's closest to initial game speed 789 // this effectively requires all players' consent to change speed 790 u32 minDiff = UINT32_MAX; 791 u32 closest = m_InitialGameSpeed; 792 for (size_t i = 0; i < m_Sessions.size(); ++i) 793 { 794 // Only count enabled players 795 if (m_PlayerAssignments[m_Sessions[i]->GetGUID()].m_Enabled) 796 { 797 u32 desired = m_Sessions[i]->GetGameSpeed(); 798 u32 diff = 0; 799 if (desired > m_InitialGameSpeed) 800 diff = desired - m_InitialGameSpeed; 801 else if (desired < m_InitialGameSpeed) 802 diff = m_InitialGameSpeed - desired; 803 804 if (diff < minDiff) 805 { 806 minDiff = diff; 807 closest = desired; 808 } 809 else if (diff == minDiff && desired < closest) 810 { 811 // Choose lowest in case of tie 812 closest = desired; 813 } 814 } 815 } 816 817 if (m_GameSpeed != closest) 818 { 819 m_GameSpeed = closest; 820 821 CSpeedChangedMessage msg; 822 msg.m_Speed = m_GameSpeed; 823 Broadcast(&msg); 824 } 825 } 826 746 827 bool CNetServerWorker::OnLoadedGame(void* context, CFsmEvent* event) 747 828 { 748 829 ENSURE(event->GetType() == (uint)NMT_LOADED_GAME); … … 750 831 CNetServerSession* session = (CNetServerSession*)context; 751 832 CNetServerWorker& server = session->GetServer(); 752 833 834 session->SetGameSpeed(server.m_GameSpeed); 835 753 836 // We're in the loading state, so wait until every player has loaded before 754 837 // starting the game 755 838 ENSURE(server.m_State == SERVER_STATE_LOADING); … … 780 863 781 864 u32 turn = message->m_CurrentTurn; 782 865 u32 readyTurn = server.m_ServerTurnManager->GetReadyTurn(); 866 u32 gameSpeed = server.m_GameSpeed; 867 session->SetGameSpeed(gameSpeed); 783 868 784 869 // Send them all commands received since their saved state, 785 870 // and turn-ended messages for any turns that have already been processed … … 794 879 CEndCommandBatchMessage endMessage; 795 880 endMessage.m_Turn = i; 796 881 endMessage.m_TurnLength = server.m_ServerTurnManager->GetSavedTurnLength(i); 882 endMessage.m_GameSpeed = gameSpeed; 797 883 session->SendMessage(&endMessage); 798 884 } 799 885 } … … 849 935 UpdateGameAttributes(m_GameAttributes); 850 936 SendPlayerAssignments(); 851 937 938 // TODO: this is yucky, but what's the alternative? 939 bool lockGameSpeed; 940 if (GetScriptInterface().HasProperty(m_GameAttributes.get(), "lockGameSpeed") && GetScriptInterface().GetProperty(m_GameAttributes.get(), "lockGameSpeed", lockGameSpeed)) 941 m_AllowGameSpeedRequests = !lockGameSpeed; 942 943 u32 gameSpeed; 944 if (GetScriptInterface().HasProperty(m_GameAttributes.get(), "gameSpeed") && GetScriptInterface().GetProperty(m_GameAttributes.get(), "gameSpeed", gameSpeed)) 945 m_InitialGameSpeed = m_GameSpeed = gameSpeed; 946 852 947 CGameStartMessage gameStart; 853 948 Broadcast(&gameStart); 854 949 } -
source/network/NetServer.h
1 /* Copyright (C) 201 1Wildfire Games.1 /* Copyright (C) 2013 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 … … 24 24 #include "ps/ThreadUtil.h" 25 25 #include "scriptinterface/ScriptVal.h" 26 26 27 #include <map> 27 28 #include <vector> 28 29 29 30 class CNetServerSession; … … 175 176 */ 176 177 bool Broadcast(const CNetMessage* message); 177 178 179 u32 GetGameSpeed(); 180 178 181 private: 179 182 friend class CNetServer; 180 183 friend class CNetFileReceiveTask_ServerRejoin; … … 244 247 static bool OnAuthenticate(void* context, CFsmEvent* event); 245 248 static bool OnInGame(void* context, CFsmEvent* event); 246 249 static bool OnChat(void* context, CFsmEvent* event); 250 static bool OnRequestSpeed(void* context, CFsmEvent* event); 247 251 static bool OnLoadedGame(void* context, CFsmEvent* event); 248 252 static bool OnJoinSyncingLoadedGame(void* context, CFsmEvent* event); 249 253 static bool OnDisconnect(void* context, CFsmEvent* event); … … 254 258 255 259 void HandleMessageReceive(const CNetMessage* message, CNetServerSession* session); 256 260 261 void UpdateGameSpeed(); 257 262 263 258 264 /** 259 265 * Internal script context for (de)serializing script messages, 260 266 * and for storing game attributes. … … 296 302 */ 297 303 std::string m_JoinSyncFile; 298 304 305 // Current game speed 306 u32 m_GameSpeed; 307 u32 m_InitialGameSpeed; 308 309 // If false, we reject RequestGameSpeed messages 310 bool m_AllowGameSpeedRequests; 311 299 312 private: 300 313 // Thread-related stuff: 301 314 -
source/network/NetSession.h
1 /* Copyright (C) 201 1Wildfire Games.1 /* Copyright (C) 2013 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 … … 122 122 u32 GetHostID() const { return m_HostID; } 123 123 void SetHostID(u32 id) { m_HostID = id; } 124 124 125 u32 GetGameSpeed() const { return m_GameSpeed; } 126 void SetGameSpeed(u32 speed) { m_GameSpeed = speed; } 127 125 128 /** 126 129 * Sends a disconnection notification to the client, 127 130 * and sends a NMT_CONNECTION_LOST message to the session FSM. … … 154 157 CStr m_GUID; 155 158 CStrW m_UserName; 156 159 u32 m_HostID; 160 u32 m_GameSpeed; 157 161 }; 158 162 159 163 #endif // NETSESSION_H -
source/network/NetTurnManager.cpp
1 /* Copyright (C) 201 2Wildfire Games.1 /* Copyright (C) 2013 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 … … 274 274 275 275 void CNetTurnManager::FinishedAllCommands(u32 turn, u32 turnLength) 276 276 { 277 NETTURN_LOG((L"FinishedAllCommands(%d, %d )\n", turn, turnLength));277 NETTURN_LOG((L"FinishedAllCommands(%d, %d, %hs)\n", turn, turnLength, gameSpeed.ToString().c_str())); 278 278 279 279 ENSURE(turn == m_ReadyTurn + 1); 280 280 m_ReadyTurn = turn; … … 396 396 CEndCommandBatchMessage msg; 397 397 msg.m_TurnLength = DEFAULT_TURN_LENGTH_MP; // TODO: why do we send this? 398 398 msg.m_Turn = turn; 399 msg.m_GameSpeed = UINT32_MAX; 399 400 m_NetClient.SendMessage(&msg); 400 401 } 401 402 … … 508 509 CEndCommandBatchMessage msg; 509 510 msg.m_TurnLength = m_TurnLength; 510 511 msg.m_Turn = m_ReadyTurn; 512 msg.m_GameSpeed = m_NetServer.GetGameSpeed(); 511 513 m_NetServer.Broadcast(&msg); 512 514 513 515 // Save the turn length in case it's needed later -
source/ps/Game.cpp
126 126 std::string mapType; 127 127 m_Simulation2->GetScriptInterface().GetProperty(attribs.get(), "mapType", mapType); 128 128 129 // Setting game speed here only affects non-networked games 130 // (NetClient will do this later for networked games) 131 u32 gameSpeed; 132 if (m_Simulation2->GetScriptInterface().HasProperty(attribs.get(), "gameSpeed") && m_Simulation2->GetScriptInterface().GetProperty(attribs.get(), "gameSpeed", gameSpeed)) 133 SetGameSpeed(gameSpeed); 134 129 135 LDR_BeginRegistering(); 130 136 131 137 RegMemFun(m_Simulation2, &CSimulation2::ProgressiveLoad, L"Simulation init", 1000); … … 268 274 return true; 269 275 270 276 const double deltaSimTime = deltaRealTime * m_SimRate; 271 277 272 278 bool ok = true; 273 279 if (deltaSimTime) 274 280 { -
source/ps/Game.h
1 /* Copyright (C) 201 1Wildfire Games.1 /* Copyright (C) 2013 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 … … 138 138 139 139 /** 140 140 * Set the simulation scale multiplier. 141 * In a networked game, this must be synced. 141 142 * 142 143 * @param simRate Float value to set m_SimRate to. 143 144 * Because m_SimRate is also used to 144 145 * scale TimeSinceLastFrame it must be 145 146 * clamped to 0.0f. 146 * */147 */ 147 148 inline void SetSimRate(float simRate) 148 { m_SimRate = std::max(simRate, 0.0f); }149 { if (isfinite(simRate)) m_SimRate = std::max(simRate, 0.0f); } 149 150 150 151 /** 152 * Set the simulation scale multiplier using integer game speed. 153 * In a networked game, this must be synced. 154 * 155 * @param speed Integer speed as percentage of normal (100=normal, 50=half speed, etc.) 156 */ 157 inline void SetGameSpeed(u32 speed) 158 { m_SimRate = (float)speed/100.0f; } 159 160 /** 161 * Get the current simulation scale multiplier in integer game speed. 162 */ 163 inline int GetGameSpeed() 164 { return m_SimRate*100; } 165 166 /** 151 167 * Replace the current turn manager. 152 168 * This class will take ownership of the pointer. 153 169 */