1 /* Copyright (C) 2017 Wildfire Games. 2 * This file is part of 0 A.D. 3 * 4 * 0 A.D. is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation, either version 2 of the License, or 7 * (at your option) any later version. 8 * 9 * 0 A.D. is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>. 16 */ 17 18 #include "precompiled.h" 19 20 #include "simulation2/system/Component.h" 21 #include "ICmpCommandQueue.h" 22 23 #include "ps/CLogger.h" 24 #include "ps/Game.h" 25 #include "ps/Profile.h" 26 #include "simulation2/system/TurnManager.h" 27 28 class CCmpCommandQueue : public ICmpCommandQueue 29 { 30 public: ClassInit(CComponentManager & UNUSED (componentManager))31 static void ClassInit(CComponentManager& UNUSED(componentManager)) 32 { 33 } 34 35 DEFAULT_COMPONENT_ALLOCATOR(CommandQueue) 36 37 std::vector<SimulationCommand> m_LocalQueue; 38 GetSchema()39 static std::string GetSchema() 40 { 41 return "<a:component type='system'/><empty/>"; 42 } 43 Init(const CParamNode & UNUSED (paramNode))44 virtual void Init(const CParamNode& UNUSED(paramNode)) 45 { 46 } 47 Deinit()48 virtual void Deinit() 49 { 50 } 51 Serialize(ISerializer & serialize)52 virtual void Serialize(ISerializer& serialize) 53 { 54 JSContext* cx = GetSimContext().GetScriptInterface().GetContext(); 55 JSAutoRequest rq(cx); 56 57 serialize.NumberU32_Unbounded("num commands", (u32)m_LocalQueue.size()); 58 for (size_t i = 0; i < m_LocalQueue.size(); ++i) 59 { 60 serialize.NumberI32_Unbounded("player", m_LocalQueue[i].player); 61 serialize.ScriptVal("data", &m_LocalQueue[i].data); 62 } 63 } 64 Deserialize(const CParamNode & UNUSED (paramNode),IDeserializer & deserialize)65 virtual void Deserialize(const CParamNode& UNUSED(paramNode), IDeserializer& deserialize) 66 { 67 JSContext* cx = GetSimContext().GetScriptInterface().GetContext(); 68 JSAutoRequest rq(cx); 69 70 u32 numCmds; 71 deserialize.NumberU32_Unbounded("num commands", numCmds); 72 for (size_t i = 0; i < numCmds; ++i) 73 { 74 i32 player; 75 JS::RootedValue data(cx); 76 deserialize.NumberI32_Unbounded("player", player); 77 deserialize.ScriptVal("data", &data); 78 m_LocalQueue.emplace_back(SimulationCommand(player, cx, data)); 79 } 80 } 81 PushLocalCommand(player_id_t player,JS::HandleValue cmd)82 virtual void PushLocalCommand(player_id_t player, JS::HandleValue cmd) 83 { 84 JSContext* cx = GetSimContext().GetScriptInterface().GetContext(); 85 JSAutoRequest rq(cx); 86 87 m_LocalQueue.emplace_back(SimulationCommand(player, cx, cmd)); 88 } 89 PostNetworkCommand(JS::HandleValue cmd1)90 virtual void PostNetworkCommand(JS::HandleValue cmd1) 91 { 92 JSContext* cx = GetSimContext().GetScriptInterface().GetContext(); 93 JSAutoRequest rq(cx); 94 95 // TODO: This is a workaround because we need to pass a MutableHandle to StringifyJSON. 96 JS::RootedValue cmd(cx, cmd1.get()); 97 98 PROFILE2_EVENT("post net command"); 99 PROFILE2_ATTR("command: %s", GetSimContext().GetScriptInterface().StringifyJSON(&cmd, false).c_str()); 100 101 // TODO: would be nicer to not use globals 102 if (g_Game && g_Game->GetTurnManager()) 103 g_Game->GetTurnManager()->PostCommand(cmd); 104 } 105 FlushTurn(const std::vector<SimulationCommand> & commands)106 virtual void FlushTurn(const std::vector<SimulationCommand>& commands) 107 { 108 const ScriptInterface& scriptInterface = GetSimContext().GetScriptInterface(); 109 JSContext* cx = scriptInterface.GetContext(); 110 JSAutoRequest rq(cx); 111 112 JS::RootedValue global(cx, scriptInterface.GetGlobalObject()); 113 std::vector<SimulationCommand> localCommands; 114 m_LocalQueue.swap(localCommands); 115 116 for (size_t i = 0; i < localCommands.size(); ++i) 117 { 118 bool ok = scriptInterface.CallFunctionVoid(global, "ProcessCommand", localCommands[i].player, localCommands[i].data); 119 if (!ok) 120 LOGERROR("Failed to call ProcessCommand() global script function"); 121 } 122 123 for (size_t i = 0; i < commands.size(); ++i) 124 { 125 bool ok = scriptInterface.CallFunctionVoid(global, "ProcessCommand", commands[i].player, commands[i].data); 126 if (!ok) 127 LOGERROR("Failed to call ProcessCommand() global script function"); 128 } 129 } 130 }; 131 132 REGISTER_COMPONENT_TYPE(CommandQueue) 133