forked from 0ad/0ad
# Refactored the networking code, and redesigned the game setup screen.
Major updates to most network classes. Simplify CNetServer so it doesn't duplicate any client behaviour; all players now run CNetClient. Remove most player/slot management from networking code. Wait for all players to finish loading before starting the simulation. Remove CGameAttributes; attributes are now just a JS object. Remove CPlayer; they are now just simulation entities. Handle player colours via simulation system. Add a default map for Atlas, so it always has something to load. Move network documentation to Doxygen. Remove lots of now-unused code. This was SVN commit r7653.
This commit is contained in:
parent
303a3ff437
commit
1c0536bf08
@ -170,6 +170,8 @@
|
||||
<color name="mustard">191 191 2</color>
|
||||
<color name="brown">159 98 24</color>
|
||||
|
||||
<color name="transparent">0 0 0 0</color>
|
||||
|
||||
<!--
|
||||
NOT YET CONVERTED NOT YET CONVERTED NOT YET CONVERTED NOT YET CONVERTED NOT YET CONVERTED NOT YET CONVERTED
|
||||
NOT YET CONVERTED NOT YET CONVERTED NOT YET CONVERTED NOT YET CONVERTED NOT YET CONVERTED NOT YET CONVERTED
|
||||
|
@ -62,21 +62,19 @@
|
||||
</sprite>
|
||||
|
||||
<sprite name="bkFillBlack">
|
||||
<image backcolor="black"
|
||||
size="0 0 100% 100%"
|
||||
/>
|
||||
<image backcolor="black"/>
|
||||
</sprite>
|
||||
|
||||
<sprite name="bkFillWhite">
|
||||
<image backcolor="white"
|
||||
size="0 0 100% 100%"
|
||||
/>
|
||||
<image backcolor="white"/>
|
||||
</sprite>
|
||||
|
||||
<sprite name="bkFillGray">
|
||||
<image backcolor="gray"
|
||||
size="0 0 100% 100%"
|
||||
/>
|
||||
<image backcolor="gray"/>
|
||||
</sprite>
|
||||
|
||||
<sprite name="bkFillDarkBlue">
|
||||
<image backcolor="63 127 210 255"/>
|
||||
</sprite>
|
||||
|
||||
<!--
|
||||
@ -146,8 +144,9 @@
|
||||
</sprite>
|
||||
|
||||
<sprite name="bkGraniteBorderBlack">
|
||||
<image texture="global/tile/granite.dds"
|
||||
size="0 0 100% 100%"
|
||||
<image texture="global/tile/granite.dds"/>
|
||||
<image
|
||||
backcolor="transparent"
|
||||
border="true"
|
||||
bordercolor="black"
|
||||
/>
|
||||
@ -481,6 +480,24 @@
|
||||
/>
|
||||
</sprite>
|
||||
|
||||
<sprite name="wheatIndentFillVeryLight">
|
||||
<image backcolor="109 122 146"
|
||||
size="0 0 100%-1 1"
|
||||
/>
|
||||
<image backcolor="220 223 228"
|
||||
size="100%-1 0 100% 100%"
|
||||
/>
|
||||
<image backcolor="220 223 228"
|
||||
size="0 100%-1 100% 100%"
|
||||
/>
|
||||
<image backcolor="109 122 146"
|
||||
size="0 0 1 100%-1"
|
||||
/>
|
||||
<image backcolor="255 255 255 160"
|
||||
size="1 1 100%-1 100%-1"
|
||||
/>
|
||||
</sprite>
|
||||
|
||||
<!--
|
||||
==========================================
|
||||
SCREENSHOT WATERMARK
|
||||
|
@ -135,6 +135,13 @@
|
||||
textcolor="black"
|
||||
/>
|
||||
|
||||
<style name="wheatInput"
|
||||
sprite="wheatIndentFillVeryLight"
|
||||
sprite_selectarea="bkFillDarkBlue"
|
||||
textcolor="black"
|
||||
textcolor_selected="white"
|
||||
/>
|
||||
|
||||
<style name="wheatArrowLeft"
|
||||
font="serif-14"
|
||||
sprite="wheatCheckBoxOpen"
|
||||
@ -207,8 +214,8 @@
|
||||
sprite2_pressed="wheatArrowDnOver"
|
||||
|
||||
buffer_zone="10"
|
||||
dropdown_size="100"
|
||||
sprite_list="textureGranite"
|
||||
dropdown_size="200"
|
||||
sprite_list="bkGraniteBorderBlack"
|
||||
sprite_selectarea="bkGrayBorderBlack"
|
||||
textcolor_selected="white"
|
||||
|
||||
|
259
binaries/data/mods/public/gui/gamesetup/gamesetup.js
Normal file
259
binaries/data/mods/public/gui/gamesetup/gamesetup.js
Normal file
@ -0,0 +1,259 @@
|
||||
// Name displayed for unassigned player slots
|
||||
const NAME_UNASSIGNED = "[color=\"90 90 90 255\"][unassigned]";
|
||||
|
||||
// Is this is a networked game, or offline
|
||||
var g_IsNetworked;
|
||||
|
||||
// Is this user in control of game settings (i.e. is a network server, or offline player)
|
||||
var g_IsController;
|
||||
|
||||
// Are we currently updating the GUI in response to network messages instead of user input
|
||||
// (and therefore shouldn't send further messages to the network)
|
||||
var g_IsInGuiUpdate;
|
||||
|
||||
// Default single-player player assignments
|
||||
var g_PlayerAssignments = { "local": { "name": "You", "player": 1 } };
|
||||
|
||||
// Default game setup attributes
|
||||
var g_GameAttributes = { "map": "Latium" };
|
||||
|
||||
// Number of players for currently selected map
|
||||
var g_MaxPlayers = 8;
|
||||
|
||||
function init(attribs)
|
||||
{
|
||||
switch (attribs.type)
|
||||
{
|
||||
case "offline":
|
||||
g_IsNetworked = false;
|
||||
g_IsController = true;
|
||||
break;
|
||||
case "server":
|
||||
g_IsNetworked = true;
|
||||
g_IsController = true;
|
||||
break;
|
||||
case "client":
|
||||
g_IsNetworked = true;
|
||||
g_IsController = false;
|
||||
break;
|
||||
default:
|
||||
error("Unexpected 'type' in gamesetup init: "+attribs.type);
|
||||
}
|
||||
|
||||
// If we're a network client, disable all the map controls
|
||||
// TODO: make them look visually disabled so it's obvious why they don't work
|
||||
if (!g_IsController)
|
||||
{
|
||||
getGUIObjectByName("mapSelection").enabled = false;
|
||||
for (var i = 0; i < g_MaxPlayers; ++i)
|
||||
getGUIObjectByName("playerAssignment["+i+"]").enabled = false;
|
||||
}
|
||||
|
||||
updatePlayerList();
|
||||
}
|
||||
|
||||
function onTick()
|
||||
{
|
||||
if (g_IsNetworked)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
var message = Engine.PollNetworkClient();
|
||||
if (!message)
|
||||
break;
|
||||
handleNetMessage(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function handleNetMessage(message)
|
||||
{
|
||||
warn("Net message: "+uneval(message));
|
||||
|
||||
switch (message.type)
|
||||
{
|
||||
case "gamesetup":
|
||||
if (message.data) // (the host gets undefined data on first connect, so skip that)
|
||||
g_GameAttributes = message.data;
|
||||
|
||||
onGameAttributesChange();
|
||||
break;
|
||||
|
||||
case "players":
|
||||
g_PlayerAssignments = message.hosts;
|
||||
updatePlayerList();
|
||||
break;
|
||||
|
||||
case "start":
|
||||
Engine.PushGuiPage("page_loading.xml", { "attribs": g_GameAttributes });
|
||||
break;
|
||||
|
||||
default:
|
||||
error("Unrecognised net message type "+message.type);
|
||||
}
|
||||
}
|
||||
|
||||
// Initialise the list control containing all the available maps
|
||||
function initMapNameList(object)
|
||||
{
|
||||
var mapPath = "maps/scenarios/"
|
||||
|
||||
// Get a list of map filenames
|
||||
var mapArray = buildDirEntList(mapPath, "*.xml", false);
|
||||
|
||||
// Alphabetically sort the list, ignoring case
|
||||
mapArray.sort(function (x, y) {
|
||||
var lowerX = x.toLowerCase();
|
||||
var lowerY = y.toLowerCase();
|
||||
if (lowerX < lowerY) return -1;
|
||||
else if (lowerX > lowerY) return 1;
|
||||
else return 0;
|
||||
});
|
||||
|
||||
// Remove the path and extension from each name, since we just want the filename
|
||||
var mapNames = [ n.substring(mapPath.length, n.length-4) for each (n in mapArray) ];
|
||||
|
||||
// Select the default map
|
||||
var selected = mapNames.indexOf(g_GameAttributes.map);
|
||||
// Default to the first element if we can't find the one we searched for
|
||||
if (selected == -1)
|
||||
selected = 0;
|
||||
|
||||
// Update the list control
|
||||
object.list = mapNames;
|
||||
object.selected = selected;
|
||||
}
|
||||
|
||||
// Called when the user selects a map from the list
|
||||
function selectMap(name)
|
||||
{
|
||||
// Avoid recursion
|
||||
if (g_IsInGuiUpdate)
|
||||
return;
|
||||
|
||||
// Network clients can't change map
|
||||
if (g_IsNetworked && !g_IsController)
|
||||
return;
|
||||
|
||||
g_GameAttributes.map = name;
|
||||
|
||||
if (g_IsNetworked)
|
||||
Engine.SetNetworkGameAttributes(g_GameAttributes);
|
||||
else
|
||||
onGameAttributesChange();
|
||||
}
|
||||
|
||||
function onGameAttributesChange()
|
||||
{
|
||||
g_IsInGuiUpdate = true;
|
||||
|
||||
var mapName = g_GameAttributes.map;
|
||||
|
||||
var mapSelectionBox = getGUIObjectByName("mapSelection");
|
||||
var mapIdx = mapSelectionBox.list.indexOf(mapName);
|
||||
mapSelectionBox.selected = mapIdx;
|
||||
|
||||
getGUIObjectByName("mapInfoName").caption = mapName;
|
||||
|
||||
var description = "Sorry, no description available.";
|
||||
|
||||
// TODO: we ought to load map descriptions from the map itself, somehow.
|
||||
// Just hardcode it now for testing.
|
||||
if (mapName == "Latium")
|
||||
{
|
||||
description = "2 players. A fertile coastal region which was the birthplace of the Roman Empire. Plentiful natural resources let you build up a city and experiment with the game’s features in relative peace. Some more description could go here if you want as long as it’s not too long and still fits on the screen.";
|
||||
}
|
||||
|
||||
getGUIObjectByName("mapInfoDescription").caption = description;
|
||||
|
||||
g_IsInGuiUpdate = false;
|
||||
}
|
||||
|
||||
function launchGame()
|
||||
{
|
||||
if (g_IsNetworked && !g_IsController)
|
||||
{
|
||||
error("Only host can start game");
|
||||
return;
|
||||
}
|
||||
|
||||
if (g_IsNetworked)
|
||||
{
|
||||
Engine.SetNetworkGameAttributes(g_GameAttributes);
|
||||
Engine.StartNetworkGame();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Find the player ID which the user has been assigned to
|
||||
var playerID = -1;
|
||||
for (var i = 0; i < g_MaxPlayers; ++i)
|
||||
{
|
||||
var assignBox = getGUIObjectByName("playerAssignment["+i+"]");
|
||||
if (assignBox.selected == 1)
|
||||
playerID = i+1;
|
||||
}
|
||||
Engine.StartGame(g_GameAttributes, playerID);
|
||||
Engine.PushGuiPage("page_loading.xml", { "attribs": g_GameAttributes });
|
||||
}
|
||||
}
|
||||
|
||||
function updatePlayerList()
|
||||
{
|
||||
g_IsInGuiUpdate = true;
|
||||
|
||||
var boxSpacing = 32;
|
||||
|
||||
var hostNameList = [NAME_UNASSIGNED];
|
||||
var hostGuidList = [""];
|
||||
var assignments = [];
|
||||
|
||||
for (var guid in g_PlayerAssignments)
|
||||
{
|
||||
var name = g_PlayerAssignments[guid].name;
|
||||
var hostID = hostNameList.length;
|
||||
hostNameList.push(name);
|
||||
hostGuidList.push(guid);
|
||||
assignments[g_PlayerAssignments[guid].player] = hostID;
|
||||
}
|
||||
|
||||
for (var i = 0; i < g_MaxPlayers; ++i)
|
||||
{
|
||||
var box = getGUIObjectByName("playerBox["+i+"]");
|
||||
var boxSize = box.size;
|
||||
var h = boxSize.bottom - boxSize.top;
|
||||
boxSize.top = i * boxSpacing;
|
||||
boxSize.bottom = i * boxSpacing + h;
|
||||
box.size = boxSize;
|
||||
|
||||
getGUIObjectByName("playerName["+i+"]").caption = "Player "+(i+1);
|
||||
|
||||
var assignBox = getGUIObjectByName("playerAssignment["+i+"]");
|
||||
assignBox.list = hostNameList;
|
||||
var selection = (assignments[i+1] || 0);
|
||||
if (assignBox.selected != selection)
|
||||
assignBox.selected = selection;
|
||||
|
||||
let playerID = i+1;
|
||||
if (g_IsNetworked && g_IsController)
|
||||
{
|
||||
assignBox.onselectionchange = function ()
|
||||
{
|
||||
if (!g_IsInGuiUpdate)
|
||||
Engine.AssignNetworkPlayer(playerID, hostGuidList[this.selected]);
|
||||
};
|
||||
}
|
||||
else if (!g_IsNetworked)
|
||||
{
|
||||
assignBox.onselectionchange = function ()
|
||||
{
|
||||
if (!g_IsInGuiUpdate)
|
||||
{
|
||||
g_PlayerAssignments[hostGuidList[this.selected]].player = playerID;
|
||||
updatePlayerList();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
g_IsInGuiUpdate = false;
|
||||
}
|
74
binaries/data/mods/public/gui/gamesetup/gamesetup.xml
Normal file
74
binaries/data/mods/public/gui/gamesetup/gamesetup.xml
Normal file
@ -0,0 +1,74 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<objects>
|
||||
|
||||
<script file="gui/gamesetup/gamesetup.js"/>
|
||||
|
||||
<!-- Add a translucent black background to fade out the menu page -->
|
||||
<object type="image" z="0" sprite="bkTranslucent"/>
|
||||
|
||||
<object type="image" style="wheatWindow" size="50%-400 50%-300 50%+400 50%+300">
|
||||
|
||||
<action on="Tick">
|
||||
onTick();
|
||||
</action>
|
||||
|
||||
<object style="wheatWindowTitleBar" type="text">Match setup</object>
|
||||
|
||||
<object type="button" style="wheatExit" tooltip_style="snToolTip">
|
||||
<action on="Press"><![CDATA[
|
||||
Engine.PopGuiPage();
|
||||
]]></action>
|
||||
</object>
|
||||
|
||||
<object size="0 0 250 100%">
|
||||
|
||||
<object name="mapSelection"
|
||||
style="wheatList"
|
||||
type="list"
|
||||
size="0 0 100% 100%-200"
|
||||
tooltip_style="onscreenToolTip"
|
||||
tooltip="Select a map to play on.">
|
||||
<action on="Load">initMapNameList(this);</action>
|
||||
<action on="SelectionChange">selectMap(this.list[this.selected]);</action>
|
||||
</object>
|
||||
|
||||
<object type="image" style="indentedPanel" size="0 100%-190 100% 100%">
|
||||
<object name="mapInfoName" type="text" size="0 0 100% 30" font="serif-bold-18">[Map name]</object>
|
||||
<object name="mapInfoDescription" type="text" size="0 24 100% 100%" font="serif-13">[Description]</object>
|
||||
</object>
|
||||
|
||||
</object>
|
||||
|
||||
<object size="260 0 100% 100%-200" type="image" sprite="wheatIndentFillLight">
|
||||
<repeat count="8">
|
||||
<object name="playerBox[n]" size="0 0 100% 30">
|
||||
<object name="playerName[n]" type="text" text_align="right" size="0 6 150 100%">[Player name]</object>
|
||||
<object name="playerAssignment[n]" type="dropdown" style="wheatDropDown" size="150 2 300 100%"/>
|
||||
</object>
|
||||
</repeat>
|
||||
</object>
|
||||
|
||||
<object name="onscreenToolTip"
|
||||
type="text"
|
||||
font="serif-14"
|
||||
textcolor="white"
|
||||
sprite="bkTranslucent"
|
||||
hidden="true"
|
||||
size="100%-300 100%-190 100% 100%-45"
|
||||
>[Tooltip text]</object>
|
||||
|
||||
<object
|
||||
type="button"
|
||||
style="wheatButton"
|
||||
font="serif-16"
|
||||
size="100%-140 100%-40 100%+3 100%+3"
|
||||
tooltip_style="onscreenToolTip"
|
||||
tooltip="Click this button to start a new game with the current settings."
|
||||
>Start game!
|
||||
<action on="Press">launchGame();</action>
|
||||
</object>
|
||||
|
||||
</object>
|
||||
|
||||
</objects>
|
69
binaries/data/mods/public/gui/gamesetup/gamesetup_mp.js
Normal file
69
binaries/data/mods/public/gui/gamesetup/gamesetup_mp.js
Normal file
@ -0,0 +1,69 @@
|
||||
var g_IsConnecting = false;
|
||||
var g_GameType; // "server" or "client"
|
||||
|
||||
function init()
|
||||
{
|
||||
}
|
||||
|
||||
function startConnectionStatus(type)
|
||||
{
|
||||
g_GameType = type;
|
||||
g_IsConnecting = true;
|
||||
getGUIObjectByName("connectionStatus").caption = "Connecting to server...";
|
||||
}
|
||||
|
||||
function onTick()
|
||||
{
|
||||
if (!g_IsConnecting)
|
||||
return;
|
||||
|
||||
while (true)
|
||||
{
|
||||
var message = Engine.PollNetworkClient();
|
||||
if (!message)
|
||||
break;
|
||||
|
||||
warn("Net message: "+uneval(message));
|
||||
|
||||
switch (message.type)
|
||||
{
|
||||
case "netstatus":
|
||||
switch (message.status)
|
||||
{
|
||||
case "connected":
|
||||
getGUIObjectByName("connectionStatus").caption = "Registering with server...";
|
||||
break;
|
||||
case "authenticated":
|
||||
Engine.PopGuiPage();
|
||||
Engine.PushGuiPage("page_gamesetup.xml", { "type": g_GameType });
|
||||
return; // don't process any more messages
|
||||
default:
|
||||
error("Unrecognised netstatus type "+message.status);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
error("Unrecognised net message type "+message.type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function switchSetupPage(oldpage, newpage)
|
||||
{
|
||||
getGUIObjectByName(oldpage).hidden = true;
|
||||
getGUIObjectByName(newpage).hidden = false;
|
||||
}
|
||||
|
||||
function startHost(playername, servername)
|
||||
{
|
||||
Engine.StartNetworkHost(playername);
|
||||
startConnectionStatus("server");
|
||||
// TODO: ought to do something(?) with servername
|
||||
}
|
||||
|
||||
function startJoin(playername, ip)
|
||||
{
|
||||
Engine.StartNetworkJoin(playername, ip);
|
||||
startConnectionStatus("client");
|
||||
}
|
156
binaries/data/mods/public/gui/gamesetup/gamesetup_mp.xml
Normal file
156
binaries/data/mods/public/gui/gamesetup/gamesetup_mp.xml
Normal file
@ -0,0 +1,156 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<objects>
|
||||
|
||||
<script file="gui/gamesetup/gamesetup_mp.js"/>
|
||||
|
||||
<!-- Add a translucent black background to fade out the menu page -->
|
||||
<object type="image" z="0" sprite="bkTranslucent"/>
|
||||
|
||||
<object type="image" style="wheatWindow" size="50%-190 50%-120 50%+190 50%+120">
|
||||
|
||||
<action on="Tick">
|
||||
onTick();
|
||||
</action>
|
||||
|
||||
<object type="text" style="wheatWindowTitleBar">
|
||||
Multiplayer
|
||||
</object>
|
||||
|
||||
<object type="button" style="wheatExit">
|
||||
<action on="Press"><![CDATA[
|
||||
Engine.PopGuiPage();
|
||||
]]></action>
|
||||
</object>
|
||||
|
||||
<object name="pageSelectMode">
|
||||
|
||||
<object type="text" size="0 50%-60 100% 50%">
|
||||
Please select whether you want to join a game or host your own game.
|
||||
</object>
|
||||
|
||||
<object type="text" size="0 50%+22 200 50%+50" style="wheatTextHeadBlack">
|
||||
Game Mode:
|
||||
</object>
|
||||
|
||||
<object type="button" style="wheatButton" size="130 50%+20 230 50%+50">
|
||||
Join Game
|
||||
<action on="Press"><![CDATA[
|
||||
switchSetupPage("pageSelectMode", "pageJoin");
|
||||
]]></action>
|
||||
</object>
|
||||
|
||||
<object type="button" style="wheatButton" size="250 50%+20 350 50%+50">
|
||||
Host Game
|
||||
<action on="Press"><![CDATA[
|
||||
switchSetupPage("pageSelectMode", "pageHost");
|
||||
]]></action>
|
||||
</object>
|
||||
|
||||
</object>
|
||||
|
||||
<object name="pageHost" hidden="true">
|
||||
|
||||
<object type="text" size="0 0 400 30">
|
||||
Setting up your server.
|
||||
</object>
|
||||
|
||||
<object type="text" size="0 40 200 70" style="wheatTextHeadBlack">
|
||||
Player name:
|
||||
</object>
|
||||
|
||||
<object name="hostPlayerName" type="input" size="220 40 370 60" style="wheatInput">
|
||||
<action on="Load"><![CDATA[
|
||||
this.caption = Engine.GetDefaultPlayerName();
|
||||
]]></action>
|
||||
</object>
|
||||
|
||||
<object type="text" size="0 80 200 110" style="wheatTextHeadBlack">
|
||||
Server name:
|
||||
</object>
|
||||
|
||||
<object name="hostServerName" type="input" size="220 80 370 100" style="wheatInput">
|
||||
<action on="Load"><![CDATA[
|
||||
this.caption = Engine.GetDefaultPlayerName() + "'s game";
|
||||
]]></action>
|
||||
</object>
|
||||
|
||||
<object type="button" size="50%-125 200 50%-25 230" style="wheatButton">
|
||||
Continue
|
||||
<action on="Press"><![CDATA[
|
||||
startHost(
|
||||
getGUIObjectByName("hostPlayerName").caption,
|
||||
getGUIObjectByName("hostServerName").caption);
|
||||
switchSetupPage("pageHost", "pageConnecting");
|
||||
]]></action>
|
||||
</object>
|
||||
|
||||
<object type="button" style="wheatButton" size="50%+25 200 50%+125 230">
|
||||
Back
|
||||
<action on="Press"><![CDATA[
|
||||
switchSetupPage("pageHost", "pageSelectMode");
|
||||
]]></action>
|
||||
</object>
|
||||
|
||||
</object>
|
||||
|
||||
<object name="pageJoin" hidden="true">
|
||||
|
||||
<object type="text" size="0 0 400 30">
|
||||
Joining an existing game.
|
||||
</object>
|
||||
|
||||
<object type="text" size="0 40 200 70" style="wheatTextHeadBlack">
|
||||
Player name:
|
||||
</object>
|
||||
|
||||
<object name="joinPlayerName" type="input" size="220 40 370 60" style="wheatInput">
|
||||
<action on="Load"><![CDATA[
|
||||
this.caption = Engine.GetDefaultPlayerName();
|
||||
]]></action>
|
||||
</object>
|
||||
|
||||
<object type="text" size="0 80 200 110" style="wheatTextHeadBlack">
|
||||
Server Hostname or IP:
|
||||
</object>
|
||||
|
||||
<object name="joinIP" type="input" size="220 80 370 100" style="wheatInput">
|
||||
127.0.0.1
|
||||
</object>
|
||||
|
||||
<object type="button" size="50%-125 200 50%-25 230" style="wheatButton">
|
||||
Continue
|
||||
<action on="Press"><![CDATA[
|
||||
startJoin(
|
||||
getGUIObjectByName("joinPlayerName").caption,
|
||||
getGUIObjectByName("joinIP").caption);
|
||||
switchSetupPage("pageJoin", "pageConnecting");
|
||||
]]></action>
|
||||
</object>
|
||||
|
||||
<object type="button" size="50%+25 200 50%+125 230" style="wheatButton">
|
||||
Back
|
||||
<action on="Press"><![CDATA[
|
||||
switchSetupPage("pageJoin", "pageSelectMode");
|
||||
]]></action>
|
||||
</object>
|
||||
</object>
|
||||
|
||||
<object name="pageConnecting" hidden="true">
|
||||
|
||||
<object name="connectionStatus" type="text" text_align="center" size="0 100 100% 120">
|
||||
[Connection status]
|
||||
</object>
|
||||
|
||||
<object type="button" size="100%-110 200 100%-10 230" style="wheatButton">
|
||||
Cancel
|
||||
<action on="Press"><![CDATA[
|
||||
error("not implemented yet");
|
||||
]]></action>
|
||||
</object>
|
||||
|
||||
</object>
|
||||
|
||||
</object>
|
||||
|
||||
</objects>
|
10
binaries/data/mods/public/gui/gamesetup/setup.xml
Normal file
10
binaries/data/mods/public/gui/gamesetup/setup.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<setup>
|
||||
<tooltip
|
||||
name="onscreenToolTip"
|
||||
use_object="onscreenToolTip"
|
||||
delay="0"
|
||||
hide_object="true"
|
||||
/>
|
||||
</setup>
|
5
binaries/data/mods/public/gui/gamesetup/sprites.xml
Normal file
5
binaries/data/mods/public/gui/gamesetup/sprites.xml
Normal file
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<sprites>
|
||||
|
||||
</sprites>
|
15
binaries/data/mods/public/gui/gamesetup/styles.xml
Normal file
15
binaries/data/mods/public/gui/gamesetup/styles.xml
Normal file
@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<styles>
|
||||
|
||||
<style name="indentedPanel"
|
||||
buffer_zone="5"
|
||||
font="serif-14"
|
||||
sprite="wheatIndentFillLight"
|
||||
textcolor="black"
|
||||
textcolor_selected="white"
|
||||
text_align="left"
|
||||
text_valign="center"
|
||||
/>
|
||||
|
||||
</styles>
|
@ -1,8 +1,11 @@
|
||||
function init()
|
||||
function init(data)
|
||||
{
|
||||
var mapName = "map";
|
||||
if (data && data.attribs)
|
||||
mapName = data.attribs.map;
|
||||
|
||||
// Set to "hourglass" cursor.
|
||||
setCursor("cursor-wait");
|
||||
console.write ("Loading " + g_GameAttributes.mapFile + " (" + g_GameAttributes.numPlayers + " players) ...");
|
||||
|
||||
// Choose random concept art for loading screen background (should depend on the selected civ later when this is specified).
|
||||
var sprite = "";
|
||||
@ -19,10 +22,10 @@ function init()
|
||||
|
||||
// janwas: main loop now sets progress / description, but that won't
|
||||
// happen until the first timeslice completes, so set initial values.
|
||||
getGUIObjectByName ("ldTitleBar").caption = "Loading Scenario ...";
|
||||
getGUIObjectByName ("ldTitleBar").caption = "Loading Scenario";
|
||||
getGUIObjectByName ("ldProgressBarText").caption = "";
|
||||
getGUIObjectByName ("ldProgressBar").caption = 0;
|
||||
getGUIObjectByName ("ldText").caption = "LOADING " + g_GameAttributes.mapFile + " ...\nPlease wait ...";
|
||||
getGUIObjectByName ("ldText").caption = "Loading " + mapName + "\nPlease wait...";
|
||||
|
||||
// Pick a random tip of the day (each line is a separate tip).
|
||||
var tipArray = readFileLines("gui/text/tips.txt");
|
||||
@ -39,13 +42,10 @@ function reallyStartGame()
|
||||
curr_music.fade(-1, 0.0, 5.0); // fade to 0 over 5 seconds
|
||||
|
||||
// This is a reserved function name that is executed by the engine when it is ready
|
||||
// to start the game (ie loading progress has reached 100%).
|
||||
// to start the game (i.e. loading progress has reached 100%).
|
||||
|
||||
// Switch GUI from loading screen to game session.
|
||||
if (Engine.IsNewSimulation())
|
||||
Engine.SwitchGuiPage("page_session_new.xml");
|
||||
else
|
||||
Engine.SwitchGuiPage("page_session.xml");
|
||||
|
||||
// Restore default cursor.
|
||||
setCursor ("arrow-default");
|
||||
|
10
binaries/data/mods/public/gui/page_gamesetup.xml
Normal file
10
binaries/data/mods/public/gui/page_gamesetup.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<page>
|
||||
<include>common/setup.xml</include>
|
||||
<include>common/styles.xml</include>
|
||||
<include>common/sprite1.xml</include>
|
||||
<include>gamesetup/setup.xml</include>
|
||||
<include>gamesetup/sprites.xml</include>
|
||||
<include>gamesetup/styles.xml</include>
|
||||
<include>gamesetup/gamesetup.xml</include>
|
||||
</page>
|
10
binaries/data/mods/public/gui/page_gamesetup_mp.xml
Normal file
10
binaries/data/mods/public/gui/page_gamesetup_mp.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<page>
|
||||
<include>common/setup.xml</include>
|
||||
<include>common/styles.xml</include>
|
||||
<include>common/sprite1.xml</include>
|
||||
<include>gamesetup/setup.xml</include>
|
||||
<include>gamesetup/sprites.xml</include>
|
||||
<include>gamesetup/styles.xml</include>
|
||||
<include>gamesetup/gamesetup_mp.xml</include>
|
||||
</page>
|
@ -130,8 +130,7 @@ Watch for updates or get involved in the development: http://wildfiregames.com/0
|
||||
>
|
||||
<action on="Press"><![CDATA[
|
||||
// Open Session Setup window.
|
||||
sessionType = "Skirmish"
|
||||
openSessionSetup ("");
|
||||
Engine.PushGuiPage("page_gamesetup.xml", { type: "offline" });
|
||||
]]></action>
|
||||
</object>
|
||||
|
||||
@ -145,7 +144,7 @@ Watch for updates or get involved in the development: http://wildfiregames.com/0
|
||||
>
|
||||
<action on="Press"><![CDATA[
|
||||
// Open Multiplayer connection window.
|
||||
openMainMenuSubWindow ("pgMPModeSel");
|
||||
Engine.PushGuiPage("page_gamesetup_mp.xml");
|
||||
]]></action>
|
||||
</object>
|
||||
|
||||
@ -350,834 +349,6 @@ Watch for updates or get involved in the development: http://wildfiregames.com/0
|
||||
|
||||
</object>
|
||||
|
||||
<!--
|
||||
===============================================
|
||||
- GAME SESSION SETUP SCREEN -
|
||||
===============================================
|
||||
-->
|
||||
|
||||
<!-- main object; the size of this equals to fullscreen at lowest resolution (800x600) -->
|
||||
<object name="pgSessionSetup"
|
||||
style="wheatWindow"
|
||||
type="image"
|
||||
hidden="true"
|
||||
size="50%-400 50%-300 50%+400 50%+300"
|
||||
>
|
||||
<object name="pgSessionSetupTitleBar"
|
||||
style="wheatWindowTitleBar"
|
||||
type="button"
|
||||
>
|
||||
</object>
|
||||
|
||||
<object name="pgSessionSetupExitButton"
|
||||
style="wheatExit"
|
||||
type="button"
|
||||
tooltip_style="snToolTip"
|
||||
>
|
||||
<action on="Press"><![CDATA[
|
||||
closeSessionSetup();
|
||||
]]></action>
|
||||
</object>
|
||||
|
||||
<object name="pgSessionSetupTitle"
|
||||
style="wheatTextHeadMaxBlack"
|
||||
type="text"
|
||||
size="10 0 150 28"
|
||||
>Game Setup
|
||||
</object>
|
||||
|
||||
|
||||
<!--
|
||||
==========================================
|
||||
- MULTIPLAYER OBJECTS
|
||||
- Not visible to Skirmish.
|
||||
==========================================
|
||||
-->
|
||||
|
||||
<object name="pgSessionSetupMP"
|
||||
hidden="true"
|
||||
>
|
||||
|
||||
<!--
|
||||
==========================================
|
||||
- HOST OBJECTS
|
||||
- Not visible to Skirmish or Client.
|
||||
==========================================
|
||||
-->
|
||||
|
||||
<object name="pgSessionSetupMPHost"
|
||||
hidden="true"
|
||||
>
|
||||
|
||||
<!-- controls for lag bars and player kicking -->
|
||||
<object name="pgSessionSetupMPKickP1"
|
||||
style="wheatButton"
|
||||
type="button"
|
||||
size="500 120 520 140"
|
||||
>K
|
||||
<action on="Press"><![CDATA[
|
||||
// Kick player here
|
||||
]]></action>
|
||||
</object>
|
||||
|
||||
</object>
|
||||
|
||||
<!--
|
||||
==========================================
|
||||
- CLIENT OBJECTS
|
||||
- Not visible to Skirmish or Host.
|
||||
==========================================
|
||||
-->
|
||||
|
||||
<object name="pgSessionSetupMPClient"
|
||||
hidden="true"
|
||||
>
|
||||
</object>
|
||||
|
||||
</object>
|
||||
|
||||
<!--
|
||||
==========================================
|
||||
- HOST/SKIRMISH OBJECTS
|
||||
- Not visible to Client.
|
||||
==========================================
|
||||
-->
|
||||
|
||||
<object name="pgSessionSetupMaster"
|
||||
hidden="true"
|
||||
>
|
||||
</object>
|
||||
|
||||
<!--
|
||||
==========================================
|
||||
- SKIRMISH OBJECTS
|
||||
- Not visible to Host or Client.
|
||||
==========================================
|
||||
-->
|
||||
|
||||
<object name="pgSessionSetupSkirmish"
|
||||
hidden="true"
|
||||
>
|
||||
</object>
|
||||
|
||||
<!--
|
||||
If objects are only supposed to be visible in multiplayer, or are only available to hosts,
|
||||
please put them in the containers above.
|
||||
-->
|
||||
|
||||
|
||||
<!--
|
||||
==========================================
|
||||
- CIV SELECTION CONTROLS
|
||||
==========================================
|
||||
-->
|
||||
|
||||
<object name="pgSessionSetupGrpCivSel"
|
||||
style="wheatBorderBlack"
|
||||
type="image"
|
||||
size="10 30 33% 50%-25"
|
||||
>
|
||||
|
||||
<object name="pgSessionSetupCivSelTxt"
|
||||
type="text"
|
||||
size="5 5 100%-5 25"
|
||||
>Your Civilization:
|
||||
</object>
|
||||
|
||||
<object name="pgSessionSetupCivSel"
|
||||
style="wheatBorderBlack"
|
||||
type="button"
|
||||
size="30 35 100%-30 100%-45"
|
||||
sprite="snEmblemHele"
|
||||
tooltip_style="pgSessionSetupToolTip"
|
||||
tooltip="Click here to change the civilization you would like to play."
|
||||
/>
|
||||
<object name="pgSessionSetupCivSelName"
|
||||
style="wheatTextHeadMaxBlack"
|
||||
type="text"
|
||||
size="30 100%-35 100%-30 100%-5"
|
||||
>Hellenes (i)
|
||||
</object>
|
||||
|
||||
</object>
|
||||
|
||||
<!--
|
||||
==========================================
|
||||
- MAP CONTROLS
|
||||
==========================================
|
||||
-->
|
||||
|
||||
<object name="pgSessionSetupGrpMap"
|
||||
style="wheatBorderBlack"
|
||||
type="image"
|
||||
size="10 50%-20 33% 100%-10"
|
||||
>
|
||||
|
||||
<!-- MAP SELECTION -->
|
||||
<object name="pgSessionSetupMapTxt"
|
||||
type="text"
|
||||
size="5 5 100%-5 25"
|
||||
>Map:
|
||||
</object>
|
||||
|
||||
|
||||
<object name="pgSessionSetupMapTypeTxt"
|
||||
type="text"
|
||||
size="5 30 100%-5 55"
|
||||
>Type:
|
||||
</object>
|
||||
|
||||
<object name="pgSessionSetupMapType"
|
||||
style="wheatDropDown"
|
||||
type="dropdown"
|
||||
size="50 25 100%-5 55"
|
||||
>
|
||||
<action on="Load"><![CDATA[
|
||||
pushItem (this.name, "Random Map");
|
||||
pushItem (this.name, "Real World Map");
|
||||
setCurrItemValue (this.name, "Random Map");
|
||||
]]></action>
|
||||
</object>
|
||||
|
||||
|
||||
<object name="pgSessionSetupMapNameTxt"
|
||||
type="text"
|
||||
size="5 65 100%-5 90"
|
||||
>Name:
|
||||
</object>
|
||||
|
||||
<object name="pgSessionSetupMapName"
|
||||
style="wheatDropDown"
|
||||
type="dropdown"
|
||||
size="50 60 100%-5 90"
|
||||
>
|
||||
<action on="Load"><![CDATA[
|
||||
// Note: We should set this in a .cfg or initialisation area, as it's used again later.
|
||||
mapPath = "maps/scenarios/"
|
||||
|
||||
// Get a list of map names.
|
||||
mapArray = buildDirEntList (mapPath, "*.xml", false);
|
||||
|
||||
// Alphabetically sort the list, ignoring case.
|
||||
mapArray.sort(function (x, y) {
|
||||
var lowerX = x.toLowerCase();
|
||||
var lowerY = y.toLowerCase();
|
||||
if(lowerX < lowerY) return -1;
|
||||
else if(lowerX > lowerY) return 1;
|
||||
else return 0;
|
||||
});
|
||||
|
||||
for (mapIndex = 0; mapIndex < mapArray.length; mapIndex++)
|
||||
{
|
||||
// Remove the path and extension from each name, since we just want the filename.
|
||||
// (Is there an equivalent "basename" function for that in JS?)
|
||||
mapArray[mapIndex] = mapArray[mapIndex].substring (mapPath.length, mapArray[mapIndex].length-4)
|
||||
|
||||
// Add map name to the end of the object's list.
|
||||
pushItem (this.name, mapArray[mapIndex]);
|
||||
}
|
||||
|
||||
// Set default value (pick whatever's at the top of the list).
|
||||
// getGUIObjectByName(this.name).selected = 0;
|
||||
// No, on second thought, set it to the randomly generated map until we can remember the last choice.
|
||||
setCurrItemValue (this.name, "Latium");
|
||||
]]></action>
|
||||
</object>
|
||||
|
||||
|
||||
<object name="pgSessionSetupFoWTxt"
|
||||
type="text"
|
||||
size="5 100 110 120"
|
||||
>Fog of War:
|
||||
</object>
|
||||
|
||||
<object name="pgSessionSetupFoW"
|
||||
style="wheatCrossBox"
|
||||
type="checkbox"
|
||||
size="120 100 140 120"
|
||||
checked="true"
|
||||
/>
|
||||
|
||||
<!--
|
||||
- LINE OF SIGHT
|
||||
-->
|
||||
|
||||
<object name="pgSessionSetupLosLabel"
|
||||
type="text"
|
||||
size="5 130 110 155"
|
||||
>LOS:
|
||||
</object>
|
||||
|
||||
<object name="pgSessionSetupLosSetting"
|
||||
style="wheatDropDown"
|
||||
type="dropdown"
|
||||
size="115 125 100%-5 155"
|
||||
>
|
||||
<item>Normal</item>
|
||||
<item>Explored</item>
|
||||
<item>All Visible</item>
|
||||
|
||||
<action on="Load"><![CDATA[
|
||||
getGUIObjectByName("pgSessionSetupLosSetting").selected = 0;
|
||||
]]></action>
|
||||
</object>
|
||||
|
||||
<!-- <object name="pgSessionSetupMapRevealTxt"
|
||||
type="text"
|
||||
size="5 135 110 155"
|
||||
>Reveal Map:
|
||||
</object>
|
||||
|
||||
<object name="pgSessionSetupMapRevealCheck"
|
||||
style="wheatCrossBox"
|
||||
type="checkbox"
|
||||
size="120 135 140 155"
|
||||
/>-->
|
||||
|
||||
|
||||
<object name="pgSessionSetupMapSizeTxt"
|
||||
type="text"
|
||||
size="5 165 110 185"
|
||||
>Map Size:
|
||||
</object>
|
||||
|
||||
<object name="pgSessionSetupMapSize"
|
||||
style="wheatDropDown"
|
||||
type="dropdown"
|
||||
size="115 160 100%-5 190"
|
||||
>
|
||||
<action on="Load"><![CDATA[
|
||||
pushItem (this.name, "Tiny");
|
||||
pushItem (this.name, "Small");
|
||||
pushItem (this.name, "Medium");
|
||||
pushItem (this.name, "Large");
|
||||
pushItem (this.name, "Very Large");
|
||||
setCurrItemValue (this.name, "Large");
|
||||
]]></action>
|
||||
</object>
|
||||
|
||||
<object name="pgSessionSetupMapLine1"
|
||||
style="wheatBorderBlack"
|
||||
type="image"
|
||||
size="5 200 100%-5 200"
|
||||
/>
|
||||
|
||||
<object name="pgSessionSetupMapInfo"
|
||||
style="wheatBorderBlack"
|
||||
type="text"
|
||||
font="serif-12"
|
||||
size="5 205 100%-5 100%-30"
|
||||
>
|
||||
[font="serif-bold-12"]Map Info[/font]
|
||||
Developed by, well, WFG.
|
||||
A large landmass with rivers, forests and coastal fishing grounds.
|
||||
</object>
|
||||
|
||||
<object name="pgSessionSetupMoreMaps"
|
||||
style="wheatButton"
|
||||
type="button"
|
||||
size="100%-135 100%-30 100%-5 100%-5"
|
||||
enabled="false"
|
||||
>
|
||||
Get more maps
|
||||
</object>
|
||||
|
||||
</object>
|
||||
|
||||
<object name="pgSessionSetupTempSq3"
|
||||
style="wheatBorderBlack"
|
||||
type="image"
|
||||
size="33%+10 45 100%-10 50%+20"
|
||||
>
|
||||
|
||||
<!--
|
||||
==========================================
|
||||
- MAIN MENU - PLAYER SLOTS
|
||||
==========================================
|
||||
-->
|
||||
|
||||
<object name="pgSessionSetupPlayerHead"
|
||||
type="text"
|
||||
size="5 5 100%-5 25"
|
||||
>Other Players:
|
||||
</object>
|
||||
|
||||
<object name="pgSessionSetupP1"
|
||||
style="wheatBorderBlack"
|
||||
type="button"
|
||||
enabled="false"
|
||||
size="5 30 205 60"
|
||||
text_align="left"
|
||||
/>
|
||||
|
||||
<object name="pgSessionSetupP2"
|
||||
style="wheatDropDown"
|
||||
type="dropdown"
|
||||
size="5 65 205 100"
|
||||
/>
|
||||
|
||||
<object name="pgSessionSetupP3"
|
||||
style="wheatDropDown"
|
||||
type="dropdown"
|
||||
size="5 105 205 140"
|
||||
/>
|
||||
|
||||
<object name="pgSessionSetupP4"
|
||||
style="wheatDropDown"
|
||||
type="dropdown"
|
||||
size="5 145 205 180"
|
||||
/>
|
||||
|
||||
<object name="pgSessionSetupP5"
|
||||
style="wheatDropDown"
|
||||
type="dropdown"
|
||||
size="5 185 205 220"
|
||||
/>
|
||||
|
||||
<object name="pgSessionSetupP6"
|
||||
style="wheatDropDown"
|
||||
type="dropdown"
|
||||
size="5 225 205 260"
|
||||
/>
|
||||
|
||||
<!-- Players 7 & 8 disabled for now due to spacing issues -->
|
||||
|
||||
<object name="pgSessionSetupP7"
|
||||
enabled="false"
|
||||
hidden="true"
|
||||
style="wheatDropDown"
|
||||
type="dropdown"
|
||||
size="5 265 205 300"
|
||||
/>
|
||||
|
||||
<object name="pgSessionSetupP8"
|
||||
enabled="false"
|
||||
hidden="true"
|
||||
style="wheatDropDown"
|
||||
type="dropdown"
|
||||
size="5 305 205 340"
|
||||
/>
|
||||
|
||||
</object>
|
||||
|
||||
<!--
|
||||
==========================================
|
||||
- GAME SETTINGS
|
||||
==========================================
|
||||
-->
|
||||
|
||||
|
||||
<object name="pgSessionSetupGrpGameSettings"
|
||||
style="wheatBorderBlack"
|
||||
type="image"
|
||||
size="33%+10 50%+30 67%-5 100%-10"
|
||||
>
|
||||
|
||||
|
||||
<object name="pgSessionSetupGameModeTxt"
|
||||
type="text"
|
||||
size="5 10 100 35"
|
||||
>Game Mode:
|
||||
</object>
|
||||
|
||||
<object name="pgSessionSetupGameMode"
|
||||
style="wheatDropDown"
|
||||
type="dropdown"
|
||||
size="110 5 100%-10 35"
|
||||
>
|
||||
<action on="Load"><![CDATA[
|
||||
pushItem (this.name, "Conquest");
|
||||
pushItem (this.name, "Score");
|
||||
setCurrItemValue (this.name, "Conquest");
|
||||
]]></action>
|
||||
</object>
|
||||
|
||||
|
||||
<object name="pgSessionSetupStartPhaseTxt"
|
||||
type="text"
|
||||
size="5 45 110 70"
|
||||
>Starting Phase:
|
||||
</object>
|
||||
|
||||
<object name="pgSessionSetupStartPhase"
|
||||
style="wheatDropDown"
|
||||
type="dropdown"
|
||||
size="110 40 100%-10 70"
|
||||
>
|
||||
<action on="Load"><![CDATA[
|
||||
pushItem (this.name, "Village");
|
||||
pushItem (this.name, "Town");
|
||||
pushItem (this.name, "City");
|
||||
setCurrItemValue (this.name, "Village");
|
||||
]]></action>
|
||||
</object>
|
||||
|
||||
|
||||
<object name="pgSessionSetupResourcesTxt"
|
||||
type="text"
|
||||
size="5 80 110 105"
|
||||
>Resources:
|
||||
</object>
|
||||
|
||||
<object name="pgSessionSetupResources"
|
||||
style="wheatDropDown"
|
||||
type="dropdown"
|
||||
size="110 75 100%-10 105"
|
||||
>
|
||||
<action on="Load"><![CDATA[
|
||||
pushItem (this.name, "Low");
|
||||
pushItem (this.name, "Normal");
|
||||
pushItem (this.name, "High");
|
||||
setCurrItemValue (this.name, "High");
|
||||
]]></action>
|
||||
</object>
|
||||
|
||||
|
||||
<object name="pgSessionSetupSettingsLine1"
|
||||
style="wheatBorderBlack"
|
||||
type="image"
|
||||
size="5 115 100%-5 115"
|
||||
/>
|
||||
|
||||
|
||||
</object>
|
||||
|
||||
|
||||
<!--
|
||||
==========================================
|
||||
- HELP AND INFO OBJECTS
|
||||
==========================================
|
||||
-->
|
||||
|
||||
<object name="pgSessionSetupGrpHelpInfo"
|
||||
style="wheatBorderBlack"
|
||||
type="image"
|
||||
size="67%+5 50%+30 100%-10 100%-80"
|
||||
>
|
||||
<object name="pgSessionSetupInfoTxt"
|
||||
type="text"
|
||||
size="10 10 100%-10 50%-10"
|
||||
>This is info text.
|
||||
</object>
|
||||
|
||||
<object name="pgSessionSetupToolTip"
|
||||
style="fancyTextHeadWhite"
|
||||
type="text"
|
||||
hidden="true"
|
||||
sprite="bkTranslucent"
|
||||
size="10 50%-5 100%-10 100%-10"
|
||||
>tooltipText
|
||||
</object>
|
||||
|
||||
</object>
|
||||
|
||||
<!--
|
||||
==========================================
|
||||
- SCREENSHOT MODE CHECKBOX
|
||||
==========================================
|
||||
-->
|
||||
|
||||
<object name="pgSessionSetupScreenshotModeTxt"
|
||||
type="text"
|
||||
size="83%-125 100%-65 83%+10 100%-45"
|
||||
>Screenshot Mode:
|
||||
</object>
|
||||
|
||||
<object name="pgSessionSetupScreenshotMode"
|
||||
style="wheatCrossBox"
|
||||
type="checkbox"
|
||||
size="83%+10 100%-65 83%+30 100%-45"
|
||||
checked="false"
|
||||
>
|
||||
<action on="Load">this.checked = Engine.IsNewSimulation()</action>
|
||||
</object>
|
||||
|
||||
|
||||
<!--
|
||||
==========================================
|
||||
- GAME SETUP - BUTTONS
|
||||
==========================================
|
||||
-->
|
||||
|
||||
<object name="pgSessionSetupStartButton"
|
||||
style="wheatButton"
|
||||
type="button"
|
||||
size="83%-125 100%-40 83%-25 100%-10"
|
||||
tooltip_style="pgSessionSetupToolTip"
|
||||
tooltip="Click this button to start a new game with the current settings."
|
||||
>Start!
|
||||
<action on="Press"><![CDATA[
|
||||
launchGame();
|
||||
]]></action>
|
||||
</object>
|
||||
|
||||
<object name="pgSessionSetupCancelButton"
|
||||
style="wheatButton"
|
||||
type="button"
|
||||
size="83%+25 100%-40 83%+125 100%-10"
|
||||
tooltip_style="pgSessionSetupToolTip"
|
||||
tooltip="Use this button if you are a coward and you don't dare to face the challenge of this game."
|
||||
>Cancel
|
||||
<action on="Press"><![CDATA[
|
||||
closeSessionSetup();
|
||||
]]></action>
|
||||
</object>
|
||||
</object>
|
||||
|
||||
<!--
|
||||
===============================================
|
||||
- MULTIPLAYER MODE SELECTION SCREEN -
|
||||
===============================================
|
||||
-->
|
||||
|
||||
<object name="pgMPModeSel"
|
||||
style="wheatWindow"
|
||||
type="text"
|
||||
hidden="true"
|
||||
size="50%-200 50%-90 50%+200 50%+90"
|
||||
>Please select whether you want to join a game or host your own game.
|
||||
<object name="pgMPModeSelTitleBar"
|
||||
style="wheatWindowTitleBar"
|
||||
type="button"
|
||||
>Multiplayer
|
||||
</object>
|
||||
|
||||
<object name="pgMPModeSelExitButton"
|
||||
style="wheatExit"
|
||||
type="button"
|
||||
>
|
||||
<action on="Press"><![CDATA[
|
||||
closeMainMenuSubWindow ("pgMPModeSel");
|
||||
]]></action>
|
||||
</object>
|
||||
|
||||
<!--
|
||||
==========================================
|
||||
- MAIN MENU - HOST / JOIN CONTROLS
|
||||
==========================================
|
||||
-->
|
||||
|
||||
<object name="pgMPModeSelHostJoinTxt"
|
||||
style="wheatTextHeadBlack"
|
||||
type="text"
|
||||
size="0 100%-70 200 199%-40"
|
||||
>Game Mode
|
||||
</object>
|
||||
|
||||
<object name="pgMPModeSelJoinButton"
|
||||
style="wheatButton"
|
||||
type="button"
|
||||
size="130 100%-70 230 100%-40"
|
||||
>Join Game
|
||||
<action on="Press"><![CDATA[
|
||||
// rudimentary client connection code; finally working now :)
|
||||
guiSwitch ("pgMPModeSel", "pgMPJoin");
|
||||
]]></action>
|
||||
</object>
|
||||
|
||||
<!-- (need code for the game start procedure here) -->
|
||||
|
||||
<object name="pgMPModeSelHostButton"
|
||||
style="wheatButton"
|
||||
type="button"
|
||||
size="250 100%-70 350 100%-40"
|
||||
>Host Game
|
||||
<action on="Press"><![CDATA[
|
||||
guiSwitch ("pgMPModeSel", "pgMPHost");
|
||||
]]></action>
|
||||
</object>
|
||||
|
||||
</object>
|
||||
|
||||
<!--
|
||||
===============================================
|
||||
- MULTIPLAYER HOST GAME SCREEN -
|
||||
===============================================
|
||||
-->
|
||||
|
||||
<object name="pgMPHost"
|
||||
style="wheatWindow"
|
||||
type="image"
|
||||
hidden="true"
|
||||
size="50%-190 50%-120 50%+190 50%+120"
|
||||
>
|
||||
<object name="pgMPHostTitleBar"
|
||||
style="wheatWindowTitleBar"
|
||||
type="button"
|
||||
>Host a game
|
||||
</object>
|
||||
|
||||
<object name="pgMPHostExitButton"
|
||||
style="wheatExit"
|
||||
type="button"
|
||||
>
|
||||
<action on="Press"><![CDATA[
|
||||
closeMainMenuSubWindow ("pgMPHost");
|
||||
]]></action>
|
||||
</object>
|
||||
|
||||
<!--
|
||||
==========================================
|
||||
- MAIN MENU - SERVER/GAME NAME CONTROLS
|
||||
==========================================
|
||||
-->
|
||||
|
||||
<object name="pgMPHostTxt1"
|
||||
type="text"
|
||||
size="0 0 400 30"
|
||||
>Setting up your server.
|
||||
</object>
|
||||
|
||||
<object name="pgMPHostGameNameTxt"
|
||||
style="wheatTextHeadBlack"
|
||||
type="text"
|
||||
size="0 40 200 60"
|
||||
>Game (Server) Name:
|
||||
</object>
|
||||
|
||||
<object name="pgMPHostGameName"
|
||||
style="wheatBorderBlack"
|
||||
type="input"
|
||||
size="220 40 370 60"
|
||||
>My Game
|
||||
</object>
|
||||
|
||||
<!--
|
||||
==========================================
|
||||
- MAIN MENU - WELCOME MESSAGE CONTROLS
|
||||
==========================================
|
||||
-->
|
||||
|
||||
<object name="pgMPHostWelcomeMsgTxt"
|
||||
style="wheatTextHeadBlack"
|
||||
type="text"
|
||||
size="0 70 200 90"
|
||||
>Welcome Message:
|
||||
</object>
|
||||
|
||||
<object name="pgMPHostTxt2"
|
||||
type="text"
|
||||
size="0 100 370 180"
|
||||
>You can enter some message which is displayed to new players upon joining here.
|
||||
</object>
|
||||
|
||||
<object name="pgMPHostWelcomeMsg"
|
||||
style="wheatBorderBlackNote"
|
||||
type="input"
|
||||
size="5 150 370 180"
|
||||
>Uhm, welcome to my game. Have fun!
|
||||
</object>
|
||||
|
||||
<!--
|
||||
==========================================
|
||||
- MAIN MENU - CONTINUE / BACK CONTROLS
|
||||
==========================================
|
||||
-->
|
||||
|
||||
<object name="pgMPHostContinueButton"
|
||||
style="wheatButton"
|
||||
type="button"
|
||||
size="50%-125 200 50%-25 230"
|
||||
>Continue
|
||||
<action on="Press"><![CDATA[
|
||||
initMPHost ("pgMPHost",
|
||||
getGUIObjectByName ("pgMPHostGameName").caption,
|
||||
getGUIObjectByName ("pgMPHostWelcomeMsg").caption);
|
||||
]]></action>
|
||||
</object>
|
||||
|
||||
<object name="pgMPHostBackButton"
|
||||
style="wheatButton"
|
||||
type="button"
|
||||
size="50%+25 200 50%+125 230"
|
||||
>Back
|
||||
<action on="Press"><![CDATA[
|
||||
guiSwitch ("pgMPHost", "pgMPModeSel");
|
||||
]]></action>
|
||||
</object>
|
||||
</object>
|
||||
|
||||
<!--
|
||||
===============================================
|
||||
- MULTIPLAYER JOIN GAME SCREEN -
|
||||
===============================================
|
||||
-->
|
||||
|
||||
<object name="pgMPJoin"
|
||||
style="wheatWindow"
|
||||
type="image"
|
||||
hidden="true"
|
||||
size="50%-190 50%-120 50%+190 50%+120"
|
||||
>
|
||||
<object name="pgMPJoinTitleBar"
|
||||
style="wheatWindowTitleBar"
|
||||
type="button"
|
||||
>Join a game
|
||||
</object>
|
||||
|
||||
<object name="pgMPJoinExitButton"
|
||||
style="wheatExit"
|
||||
type="button"
|
||||
>
|
||||
<action on="Press"><![CDATA[
|
||||
closeMainMenuSubWindow ("pgMPJoin");
|
||||
]]></action>
|
||||
</object>
|
||||
|
||||
<!--
|
||||
==========================================
|
||||
- MAIN MENU - SERVER NAME / IP CONTROLS
|
||||
==========================================
|
||||
-->
|
||||
|
||||
<object name="pgMPJoinTxt1"
|
||||
type="text"
|
||||
size="0 0 400 30"
|
||||
>Joining an existing game.
|
||||
</object>
|
||||
|
||||
<object name="pgMPJoinServerIPTxt"
|
||||
style="wheatTextHeadBlack"
|
||||
type="text"
|
||||
size="0 40 200 60"
|
||||
>Server Hostname or IP:
|
||||
</object>
|
||||
|
||||
<object name="pgMPJoinServerIP"
|
||||
style="wheatBorderBlack"
|
||||
type="input"
|
||||
size="220 40 370 60"
|
||||
>127.0.0.1
|
||||
</object>
|
||||
|
||||
<!--
|
||||
==========================================
|
||||
- MAIN MENU - CONTINUE / BACK CONTROLS
|
||||
==========================================
|
||||
-->
|
||||
|
||||
<object name="pgMPJoinSetupButton"
|
||||
style="wheatButton"
|
||||
type="button"
|
||||
size="50%-125 200 50%-25 230"
|
||||
>Continue
|
||||
<action on="Press"><![CDATA[
|
||||
initMPClient ("pgMPJoin",
|
||||
getGUIObjectByName ("pgMPJoinServerIP").caption);
|
||||
]]></action>
|
||||
</object>
|
||||
|
||||
<object name="pgMPJoinBackButton"
|
||||
style="wheatButton"
|
||||
type="button"
|
||||
size="50%+25 200 50%+125 230"
|
||||
>Back
|
||||
<action on="Press"><![CDATA[
|
||||
guiSwitch ("pgMPJoin", "pgMPModeSel");
|
||||
]]></action>
|
||||
</object>
|
||||
</object>
|
||||
|
||||
<!--
|
||||
===============================================
|
||||
- OPTIONS SCREEN -
|
||||
|
@ -31,6 +31,24 @@ function getHotloadData()
|
||||
return { selection: g_Selection.selected };
|
||||
}
|
||||
|
||||
function changeNetStatus(status)
|
||||
{
|
||||
var obj = getGUIObjectByName("netStatus");
|
||||
switch (status)
|
||||
{
|
||||
case "normal":
|
||||
obj.caption = "";
|
||||
obj.hidden = true;
|
||||
break;
|
||||
case "waiting_for_connect":
|
||||
obj.caption = "Waiting for other players to connect";
|
||||
obj.hidden = false;
|
||||
break;
|
||||
default:
|
||||
error("Unexpected net status "+status);
|
||||
}
|
||||
}
|
||||
|
||||
function onTick()
|
||||
{
|
||||
g_DevSettings.controlAll = getGUIObjectByName("devControlAll").checked;
|
||||
@ -92,6 +110,9 @@ function updatePlayerDisplay(simState)
|
||||
{
|
||||
var playerState = simState.players[Engine.GetPlayerID()];
|
||||
|
||||
if (!playerState)
|
||||
return;
|
||||
|
||||
getGUIObjectByName("resourceFood").caption = playerState.resourceCounts.food;
|
||||
getGUIObjectByName("resourceWood").caption = playerState.resourceCounts.wood;
|
||||
getGUIObjectByName("resourceStone").caption = playerState.resourceCounts.stone;
|
||||
|
@ -466,4 +466,6 @@
|
||||
<object name="bandbox" type="image" sprite="bandbox" ghost="true" hidden="true" z="100"
|
||||
/>
|
||||
|
||||
<object name="netStatus" type="text" style="netStatus" z="100" hidden="true"/>
|
||||
|
||||
</objects>
|
||||
|
@ -138,4 +138,8 @@
|
||||
<image backcolor="0 0 0 85"/>
|
||||
</sprite>
|
||||
|
||||
<sprite name="netStatusBackground">
|
||||
<image backcolor="0 0 0 195"/>
|
||||
</sprite>
|
||||
|
||||
</sprites>
|
||||
|
@ -103,4 +103,12 @@
|
||||
text_valign="center"
|
||||
/>
|
||||
|
||||
<style name="netStatus"
|
||||
font="serif-bold-18"
|
||||
textcolor="255 255 255"
|
||||
text_align="center"
|
||||
text_valign="center"
|
||||
sprite="netStatusBackground"
|
||||
/>
|
||||
|
||||
</styles>
|
||||
|
BIN
binaries/data/mods/public/maps/scenarios/_default.xml
(Stored with Git LFS)
Normal file
BIN
binaries/data/mods/public/maps/scenarios/_default.xml
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -6,8 +6,9 @@ Player.prototype.Schema =
|
||||
Player.prototype.Init = function()
|
||||
{
|
||||
this.playerID = undefined;
|
||||
this.playerName = "Unknown";
|
||||
this.civ = "celt";
|
||||
this.name = "Unknown";
|
||||
this.civ = "gaia";
|
||||
this.colour = { "r": 0.0, "g": 0.0, "b": 0.0, "a": 1.0 };
|
||||
this.popCount = 0;
|
||||
this.popLimit = 50;
|
||||
this.resourceCount = {
|
||||
@ -18,20 +19,32 @@ Player.prototype.Init = function()
|
||||
};
|
||||
};
|
||||
|
||||
Player.prototype.GetColour = function()
|
||||
{
|
||||
// TODO: need proper colour support
|
||||
if (this.playerID == 1)
|
||||
return { "r": 0.0, "g": 0.0, "b": 1.0, "a": 1.0 };
|
||||
else
|
||||
return { "r": 1.0, "g": 0.0, "b": 0.0, "a": 1.0 };
|
||||
};
|
||||
|
||||
Player.prototype.SetPlayerID = function(id)
|
||||
{
|
||||
this.playerID = id;
|
||||
};
|
||||
|
||||
Player.prototype.SetName = function(name)
|
||||
{
|
||||
this.name = name;
|
||||
};
|
||||
|
||||
Player.prototype.SetCiv = function(civcode)
|
||||
{
|
||||
this.civ = civcode;
|
||||
};
|
||||
|
||||
Player.prototype.SetColour = function(r, g, b)
|
||||
{
|
||||
this.colour = { "r": r/255.0, "g": g/255.0, "b": b/255.0, "a": 1.0 };
|
||||
};
|
||||
|
||||
Player.prototype.GetColour = function()
|
||||
{
|
||||
return this.colour;
|
||||
};
|
||||
|
||||
|
||||
Player.prototype.GetPopulationCount = function()
|
||||
{
|
||||
return this.popCount;
|
||||
|
14
binaries/data/mods/public/simulation/data/players.xml
Normal file
14
binaries/data/mods/public/simulation/data/players.xml
Normal file
@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!-- Default player colours -->
|
||||
<players>
|
||||
<player id="0" name="Gaia" civ="gaia" r="255" g="255" b="255"/> <!-- white -->
|
||||
<player id="1" name="Player 1" civ="celt" r="80" g="80" b="200"/> <!-- blue -->
|
||||
<player id="2" name="Player 2" civ="hele" r="150" g="20" b="20"/> <!-- red -->
|
||||
<player id="3" name="Player 3" civ="celt" r="50" g="165" b="5"/> <!-- green -->
|
||||
<player id="4" name="Player 4" civ="hele" r="230" g="230" b="75"/> <!-- yellow -->
|
||||
<player id="5" name="Player 5" civ="celt" r="50" g="170" b="170"/> <!-- turquoise -->
|
||||
<player id="6" name="Player 6" civ="hele" r="160" g="80" b="200"/> <!-- purple -->
|
||||
<player id="7" name="Player 7" civ="celt" r="235" g="120" b="20"/> <!-- orange -->
|
||||
<player id="8" name="Player 8" civ="hele" r="64" g="64" b="64"/> <!-- charcoal -->
|
||||
</players>
|
@ -7,6 +7,10 @@ function ProcessCommand(player, cmd)
|
||||
|
||||
switch (cmd.type)
|
||||
{
|
||||
case "debug-print":
|
||||
print(cmd.message);
|
||||
break;
|
||||
|
||||
case "walk":
|
||||
for each (var ent in cmd.entities)
|
||||
{
|
||||
@ -120,7 +124,7 @@ function ProcessCommand(player, cmd)
|
||||
break;
|
||||
|
||||
default:
|
||||
print("Ignoring unrecognised command type '" + cmd.type + "'\n");
|
||||
error("Ignoring unrecognised command type '" + cmd.type + "'");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,16 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!-- List of default players for single-player maps -->
|
||||
<players>
|
||||
|
||||
<player name="Gaia" rgb="255 255 255"/> <!-- white -->
|
||||
<player name="Wijitmaker" rgb="80 80 200"/> <!-- blue -->
|
||||
<player name="Matei" rgb="150 20 20"/> <!-- red -->
|
||||
<player name="Aeros" rgb="50 165 5"/> <!-- green -->
|
||||
<player name="Mythos_Ruler" rgb="230 230 75"/> <!-- yellow -->
|
||||
<player name="Hyborian" rgb="50 170 170"/> <!-- turquoise -->
|
||||
<player name="Ykkrosh" rgb="160 80 200"/> <!-- purple -->
|
||||
<player name="Pyrolink" rgb="235 120 20"/> <!-- orange -->
|
||||
<player name="Vaevictus_Music" rgb="64 64 64"/> <!-- charcoal -->
|
||||
|
||||
</players>
|
BIN
binaries/data/mods/public/temp/terrain.png
(Stored with Git LFS)
BIN
binaries/data/mods/public/temp/terrain.png
(Stored with Git LFS)
Binary file not shown.
@ -71,8 +71,8 @@ end
|
||||
-- * defines: a table of symbols to define
|
||||
extern_lib_defs = {
|
||||
boost = {
|
||||
unix_names = { "boost_signals-mt", "boost_filesystem-mt", "boost_system-mt" },
|
||||
osx_names = { "boost_signals-mt", "boost_filesystem-mt", "boost_system-mt" }
|
||||
unix_names = { "boost_signals-mt", "boost_filesystem-mt", "boost_system-mt", "boost_random-mt" },
|
||||
osx_names = { "boost_signals-mt", "boost_filesystem-mt", "boost_system-mt", "boost_random-mt" }
|
||||
},
|
||||
cryptopp = {
|
||||
win_names = { "cryptopp" },
|
||||
|
@ -3,7 +3,7 @@ PROJECT_NUMBER = trunk
|
||||
|
||||
TAB_SIZE = 4
|
||||
|
||||
INPUT = ../../source/simulation2 ../../source/scriptinterface
|
||||
INPUT = ../../source/simulation2 ../../source/scriptinterface ../../source/network
|
||||
INCLUDE_PATH = ../../source
|
||||
EXAMPLE_PATH = ../../source
|
||||
RECURSIVE = YES
|
||||
|
@ -244,7 +244,7 @@ void CGameViewImpl::ScriptingInit()
|
||||
CJSObject<CGameViewImpl>::ScriptingInit("GameView");
|
||||
}
|
||||
|
||||
int CGameView::Initialize(CGameAttributes* UNUSED(pAttribs))
|
||||
int CGameView::Initialize()
|
||||
{
|
||||
CFG_GET_SYS_VAL( "view.scroll.speed", Float, m->ViewScrollSpeed );
|
||||
CFG_GET_SYS_VAL( "view.rotate.speed", Float, m->ViewRotateSensitivity );
|
||||
@ -267,10 +267,10 @@ int CGameView::Initialize(CGameAttributes* UNUSED(pAttribs))
|
||||
|
||||
|
||||
|
||||
void CGameView::RegisterInit(CGameAttributes *pAttribs)
|
||||
void CGameView::RegisterInit()
|
||||
{
|
||||
// CGameView init
|
||||
RegMemFun1(this, &CGameView::Initialize, pAttribs, L"CGameView init", 1);
|
||||
RegMemFun(this, &CGameView::Initialize, L"CGameView init", 1);
|
||||
|
||||
// previously done by CGameView::InitResources
|
||||
RegMemFun(g_TexMan.GetSingletonPtr(), &CTextureManager::LoadTerrainTextures, L"LoadTerrainTextures", 60);
|
||||
|
@ -27,7 +27,6 @@ extern float g_YMinOffset;
|
||||
#include "lib/input.h" // InReaction - can't forward-declare enum
|
||||
|
||||
class CGame;
|
||||
class CGameAttributes;
|
||||
class CObjectManager;
|
||||
class CCamera;
|
||||
class CCinemaManager;
|
||||
@ -67,8 +66,8 @@ public:
|
||||
|
||||
void SetViewport(const SViewPort& vp);
|
||||
|
||||
void RegisterInit(CGameAttributes *pAttribs);
|
||||
int Initialize(CGameAttributes *pGameAttributes);
|
||||
void RegisterInit();
|
||||
int Initialize();
|
||||
|
||||
CObjectManager& GetObjectManager() const;
|
||||
|
||||
|
@ -269,9 +269,12 @@ private:
|
||||
// # entities+nonentities processed and total (for progress calc)
|
||||
int completed_jobs, total_jobs;
|
||||
|
||||
// maximum used entity ID, so we can safely allocate new ones
|
||||
entity_id_t max_uid;
|
||||
|
||||
void Init(const VfsPath& xml_filename);
|
||||
|
||||
void ReadPlayers();
|
||||
void ReadTerrain(XMBElement parent);
|
||||
void ReadEnvironment(XMBElement parent);
|
||||
void ReadCamera(XMBElement parent);
|
||||
@ -325,26 +328,73 @@ void CXMLReader::Init(const VfsPath& xml_filename)
|
||||
|
||||
// Find the maximum entity ID, so we can safely allocate new IDs without conflicts
|
||||
|
||||
int max_uid = SYSTEM_ENTITY;
|
||||
max_uid = SYSTEM_ENTITY;
|
||||
|
||||
XMBElement ents = nodes.GetFirstNamedItem(xmb_file.GetElementID("Entities"));
|
||||
XERO_ITER_EL(ents, ent)
|
||||
{
|
||||
utf16string uid = ent.GetAttributes().GetNamedItem(at_uid);
|
||||
max_uid = std::max(max_uid, CStr(uid).ToInt());
|
||||
max_uid = std::max(max_uid, (entity_id_t)CStr(uid).ToInt());
|
||||
}
|
||||
|
||||
// Initialise player data
|
||||
ReadPlayers();
|
||||
}
|
||||
|
||||
|
||||
void CXMLReader::ReadPlayers()
|
||||
{
|
||||
CmpPtr<ICmpPlayerManager> cmpPlayerMan(*m_MapReader.pSimulation2, SYSTEM_ENTITY);
|
||||
debug_assert(!cmpPlayerMan.null());
|
||||
|
||||
// TODO: this should be loaded from the XML instead
|
||||
size_t numPlayers = 4;
|
||||
// TODO: we ought to read at least some of this data from the map file.
|
||||
// For now, just always use the defaults.
|
||||
|
||||
std::map<int, CStrW> playerDefaultNames;
|
||||
std::map<int, CStrW> playerDefaultCivs;
|
||||
std::map<int, SColor3ub> playerDefaultColours;
|
||||
|
||||
CXeromyces playerDefaultFile;
|
||||
if (playerDefaultFile.Load(L"simulation/data/players.xml") != PSRETURN_OK)
|
||||
throw PSERROR_File_ReadFailed();
|
||||
|
||||
#define AT(x) int at_##x = playerDefaultFile.GetAttributeID(#x)
|
||||
AT(id);
|
||||
AT(name);
|
||||
AT(civ);
|
||||
AT(r); AT(g); AT(b);
|
||||
#undef AT
|
||||
|
||||
XERO_ITER_EL(playerDefaultFile.GetRoot(), player)
|
||||
{
|
||||
XMBAttributeList attrs = player.GetAttributes();
|
||||
int id = CStr(attrs.GetNamedItem(at_id)).ToInt();
|
||||
|
||||
playerDefaultNames[id] = attrs.GetNamedItem(at_name);
|
||||
playerDefaultCivs[id] = attrs.GetNamedItem(at_civ);
|
||||
|
||||
SColor3ub colour;
|
||||
colour.R = (u8)CStr(attrs.GetNamedItem(at_r)).ToInt();
|
||||
colour.G = (u8)CStr(attrs.GetNamedItem(at_g)).ToInt();
|
||||
colour.B = (u8)CStr(attrs.GetNamedItem(at_b)).ToInt();
|
||||
playerDefaultColours[id] = colour;
|
||||
}
|
||||
|
||||
size_t numPlayers = 9; // including Gaia
|
||||
|
||||
for (size_t i = 0; i < numPlayers; ++i)
|
||||
{
|
||||
int uid = ++max_uid;
|
||||
entity_id_t ent = m_MapReader.pSimulation2->AddEntity(L"special/player", uid);
|
||||
CmpPtr<ICmpPlayer> cmpPlayer(*m_MapReader.pSimulation2, ent);
|
||||
debug_assert(!cmpPlayer.null());
|
||||
|
||||
cmpPlayer->SetName(playerDefaultNames[i]);
|
||||
cmpPlayer->SetCiv(playerDefaultCivs[i]);
|
||||
|
||||
SColor3ub colour = playerDefaultColours[i];
|
||||
cmpPlayer->SetColour(colour.R, colour.G, colour.B);
|
||||
|
||||
cmpPlayerMan->AddPlayer(ent);
|
||||
}
|
||||
}
|
||||
@ -1013,10 +1063,6 @@ int CXMLReader::ReadOldEntities(XMBElement parent, double end_time)
|
||||
debug_warn(L"Invalid map XML data");
|
||||
}
|
||||
|
||||
CPlayer* player = NULL;
|
||||
if (g_Game)
|
||||
player = g_Game->GetPlayer(PlayerID);
|
||||
|
||||
// The old version uses a flat entity naming system, so we need
|
||||
// to translate it into the hierarchical filename
|
||||
if (TemplateName.Find(L"flora") == 0 || TemplateName.Find(L"fauna") == 0 || TemplateName.Find(L"geology") == 0 || TemplateName.Find(L"special") == 0)
|
||||
|
@ -35,8 +35,8 @@
|
||||
|
||||
#include "maths/MathUtil.h"
|
||||
#include "maths/NUSpline.h"
|
||||
#include "ps/Game.h"
|
||||
#include "ps/Loader.h"
|
||||
#include "ps/Player.h"
|
||||
#include "ps/Filesystem.h"
|
||||
#include "ps/XML/XMLWriter.h"
|
||||
#include "renderer/SkyManager.h"
|
||||
|
@ -17,9 +17,9 @@
|
||||
|
||||
#include "precompiled.h"
|
||||
|
||||
#include "lib/ogl.h"
|
||||
#include "Material.h"
|
||||
#include "ps/Player.h"
|
||||
|
||||
#include "lib/ogl.h"
|
||||
#include "ps/Game.h"
|
||||
#include "ps/Overlay.h" // for CColor
|
||||
|
||||
@ -106,13 +106,9 @@ SMaterialColor CMaterial::GetPlayerColor()
|
||||
|
||||
if (m_PlayerID <= PLAYER_ID_LAST_VALID)
|
||||
{
|
||||
CPlayer* player = g_Game->GetPlayer(m_PlayerID);
|
||||
if (player)
|
||||
{
|
||||
const SPlayerColour& c (player->GetColour());
|
||||
CColor c(g_Game->GetPlayerColour(m_PlayerID));
|
||||
return SMaterialColor(c.r, c.g, c.b, c.a);
|
||||
}
|
||||
}
|
||||
|
||||
// Oops, something failed.
|
||||
return BrokenColor;
|
||||
|
@ -26,9 +26,6 @@
|
||||
#include "SkeletonAnimDef.h"
|
||||
#include "UnitAnimation.h"
|
||||
|
||||
#include "ps/Game.h"
|
||||
#include "ps/Player.h"
|
||||
|
||||
CUnit::CUnit(CObjectEntry* object, CObjectManager& objectManager,
|
||||
const std::set<CStr>& actorSelections)
|
||||
: m_Object(object), m_Model(object->m_Model->Clone()),
|
||||
|
@ -35,7 +35,8 @@
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// CUnitManager constructor
|
||||
CUnitManager::CUnitManager()
|
||||
CUnitManager::CUnitManager() :
|
||||
m_ObjectManager(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -33,9 +33,7 @@
|
||||
#include "lib/bits.h"
|
||||
#include "lib/timer.h"
|
||||
#include "lib/sysdep/cpu.h"
|
||||
#include "network/NetMessage.h"
|
||||
#include "ps/Game.h"
|
||||
#include "ps/Player.h"
|
||||
#include "ps/Profile.h"
|
||||
#include "ps/World.h"
|
||||
#include "renderer/Renderer.h"
|
||||
|
@ -22,11 +22,14 @@
|
||||
#include "graphics/Camera.h"
|
||||
#include "graphics/GameView.h"
|
||||
#include "gui/GUIManager.h"
|
||||
#include "lib/sysdep/sysdep.h"
|
||||
#include "maths/FixedVector3D.h"
|
||||
#include "network/NetClient.h"
|
||||
#include "network/NetServer.h"
|
||||
#include "network/NetTurnManager.h"
|
||||
#include "ps/CLogger.h"
|
||||
#include "ps/Game.h"
|
||||
#include "ps/Overlay.h"
|
||||
#include "ps/Player.h"
|
||||
#include "ps/GameSetup/Config.h"
|
||||
#include "simulation2/Simulation2.h"
|
||||
#include "simulation2/components/ICmpCommandQueue.h"
|
||||
@ -34,6 +37,8 @@
|
||||
#include "simulation2/components/ICmpTemplateManager.h"
|
||||
#include "simulation2/helpers/Selection.h"
|
||||
|
||||
#include "js/jsapi.h"
|
||||
|
||||
/*
|
||||
* This file defines a set of functions that are available to GUI scripts, to allow
|
||||
* interaction with the rest of the engine.
|
||||
@ -82,8 +87,8 @@ CScriptVal GuiInterfaceCall(void* cbdata, std::wstring name, CScriptVal data)
|
||||
return JSVAL_VOID;
|
||||
|
||||
int player = -1;
|
||||
if (g_Game && g_Game->GetLocalPlayer())
|
||||
player = g_Game->GetLocalPlayer()->GetPlayerID();
|
||||
if (g_Game)
|
||||
player = g_Game->GetPlayerID();
|
||||
|
||||
CScriptValRooted arg (sim->GetScriptInterface().GetContext(), sim->GetScriptInterface().CloneValueFromOtherContext(guiManager->GetScriptInterface(), data.get()));
|
||||
CScriptVal ret (gui->ScriptCall(player, name, arg.get()));
|
||||
@ -133,11 +138,107 @@ std::wstring SetCursor(void* UNUSED(cbdata), std::wstring name)
|
||||
|
||||
int GetPlayerID(void* UNUSED(cbdata))
|
||||
{
|
||||
if (g_Game && g_Game->GetLocalPlayer())
|
||||
return g_Game->GetLocalPlayer()->GetPlayerID();
|
||||
if (g_Game)
|
||||
return g_Game->GetPlayerID();
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::wstring GetDefaultPlayerName(void* UNUSED(cbdata))
|
||||
{
|
||||
// TODO: this should come from a config file or something
|
||||
std::wstring name = sys_get_user_name();
|
||||
if (name.empty())
|
||||
name = L"anonymous";
|
||||
return name;
|
||||
}
|
||||
|
||||
void StartNetworkGame(void* UNUSED(cbdata))
|
||||
{
|
||||
debug_assert(g_NetServer);
|
||||
g_NetServer->StartGame();
|
||||
}
|
||||
|
||||
void StartGame(void* cbdata, CScriptVal attribs, int playerID)
|
||||
{
|
||||
CGUIManager* guiManager = static_cast<CGUIManager*> (cbdata);
|
||||
|
||||
debug_assert(!g_NetServer);
|
||||
debug_assert(!g_NetClient);
|
||||
|
||||
debug_assert(!g_Game);
|
||||
g_Game = new CGame();
|
||||
|
||||
// Convert from GUI script context to sim script context
|
||||
CSimulation2* sim = g_Game->GetSimulation2();
|
||||
CScriptValRooted gameAttribs (sim->GetScriptInterface().GetContext(),
|
||||
sim->GetScriptInterface().CloneValueFromOtherContext(guiManager->GetScriptInterface(), attribs.get()));
|
||||
|
||||
g_Game->SetPlayerID(playerID);
|
||||
g_Game->StartGame(gameAttribs);
|
||||
}
|
||||
|
||||
void SetNetworkGameAttributes(void* cbdata, CScriptVal attribs)
|
||||
{
|
||||
CGUIManager* guiManager = static_cast<CGUIManager*> (cbdata);
|
||||
|
||||
debug_assert(g_NetServer);
|
||||
|
||||
// Convert from GUI script context to net server script context
|
||||
CScriptValRooted gameAttribs (g_NetServer->GetScriptInterface().GetContext(),
|
||||
g_NetServer->GetScriptInterface().CloneValueFromOtherContext(guiManager->GetScriptInterface(), attribs.get()));
|
||||
|
||||
g_NetServer->UpdateGameAttributes(gameAttribs);
|
||||
}
|
||||
|
||||
void StartNetworkHost(void* UNUSED(cbdata), std::wstring playerName)
|
||||
{
|
||||
debug_assert(!g_NetClient);
|
||||
debug_assert(!g_NetServer);
|
||||
debug_assert(!g_Game);
|
||||
|
||||
g_NetServer = new CNetServer();
|
||||
bool ok = g_NetServer->SetupConnection();
|
||||
debug_assert(ok); // TODO: need better error handling
|
||||
|
||||
g_Game = new CGame();
|
||||
g_NetClient = new CNetClient(g_Game);
|
||||
g_NetClient->SetUserName(playerName);
|
||||
g_NetClient->SetupLocalConnection(*g_NetServer);
|
||||
}
|
||||
|
||||
void StartNetworkJoin(void* UNUSED(cbdata), std::wstring playerName, std::string serverAddress)
|
||||
{
|
||||
debug_assert(!g_NetClient);
|
||||
debug_assert(!g_NetServer);
|
||||
debug_assert(!g_Game);
|
||||
|
||||
g_Game = new CGame();
|
||||
g_NetClient = new CNetClient(g_Game);
|
||||
g_NetClient->SetUserName(playerName);
|
||||
g_NetClient->SetupConnection(serverAddress);
|
||||
}
|
||||
|
||||
// TODO: we need some way to disconnect the server/client
|
||||
|
||||
CScriptVal PollNetworkClient(void* cbdata)
|
||||
{
|
||||
CGUIManager* guiManager = static_cast<CGUIManager*> (cbdata);
|
||||
|
||||
debug_assert(g_NetClient);
|
||||
|
||||
CScriptValRooted poll = g_NetClient->GuiPoll();
|
||||
|
||||
// Convert from net client context to GUI script context
|
||||
return guiManager->GetScriptInterface().CloneValueFromOtherContext(g_NetClient->GetScriptInterface(), poll.get());
|
||||
}
|
||||
|
||||
void AssignNetworkPlayer(void* UNUSED(cbdata), int playerID, std::string guid)
|
||||
{
|
||||
debug_assert(g_NetServer);
|
||||
|
||||
g_NetServer->AssignPlayer(playerID, guid);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void GuiScriptingInit(ScriptInterface& scriptInterface)
|
||||
@ -158,7 +259,17 @@ void GuiScriptingInit(ScriptInterface& scriptInterface)
|
||||
scriptInterface.RegisterFunction<std::vector<entity_id_t>, int, int, int, int, int, &PickFriendlyEntitiesInRect>("PickFriendlyEntitiesInRect");
|
||||
scriptInterface.RegisterFunction<CFixedVector3D, int, int, &GetTerrainAtPoint>("GetTerrainAtPoint");
|
||||
|
||||
// Network / game setup functions
|
||||
scriptInterface.RegisterFunction<void, &StartNetworkGame>("StartNetworkGame");
|
||||
scriptInterface.RegisterFunction<void, CScriptVal, int, &StartGame>("StartGame");
|
||||
scriptInterface.RegisterFunction<void, std::wstring, &StartNetworkHost>("StartNetworkHost");
|
||||
scriptInterface.RegisterFunction<void, std::wstring, std::string, &StartNetworkJoin>("StartNetworkJoin");
|
||||
scriptInterface.RegisterFunction<CScriptVal, &PollNetworkClient>("PollNetworkClient");
|
||||
scriptInterface.RegisterFunction<void, CScriptVal, &SetNetworkGameAttributes>("SetNetworkGameAttributes");
|
||||
scriptInterface.RegisterFunction<void, int, std::string, &AssignNetworkPlayer>("AssignNetworkPlayer");
|
||||
|
||||
// Misc functions
|
||||
scriptInterface.RegisterFunction<std::wstring, std::wstring, &SetCursor>("SetCursor");
|
||||
scriptInterface.RegisterFunction<int, &GetPlayerID>("GetPlayerID");
|
||||
scriptInterface.RegisterFunction<std::wstring, &GetDefaultPlayerName>("GetDefaultPlayerName");
|
||||
}
|
||||
|
@ -41,25 +41,27 @@ that of Atlas depending on commandline parameters.
|
||||
#include "lib/external_libraries/sdl.h"
|
||||
#include "lib/res/sound/snd_mgr.h"
|
||||
|
||||
#include "ps/CConsole.h"
|
||||
#include "ps/Filesystem.h"
|
||||
#include "ps/Game.h"
|
||||
#include "ps/Globals.h"
|
||||
#include "ps/Hotkey.h"
|
||||
#include "ps/Loader.h"
|
||||
#include "ps/Profile.h"
|
||||
#include "ps/Pyrogenesis.h"
|
||||
#include "ps/Util.h"
|
||||
#include "ps/VideoMode.h"
|
||||
#include "ps/GameSetup/GameSetup.h"
|
||||
#include "ps/GameSetup/Atlas.h"
|
||||
#include "ps/GameSetup/Config.h"
|
||||
#include "ps/GameSetup/CmdLineArgs.h"
|
||||
#include "ps/Loader.h"
|
||||
#include "ps/Filesystem.h"
|
||||
#include "ps/CConsole.h"
|
||||
#include "ps/Profile.h"
|
||||
#include "ps/Util.h"
|
||||
#include "ps/Game.h"
|
||||
#include "ps/Hotkey.h"
|
||||
#include "ps/Globals.h"
|
||||
#include "ps/VideoMode.h"
|
||||
#include "ps/XML/Xeromyces.h"
|
||||
#include "network/NetClient.h"
|
||||
#include "network/NetServer.h"
|
||||
#include "network/NetSession.h"
|
||||
#include "graphics/Camera.h"
|
||||
#include "graphics/GameView.h"
|
||||
#include "scripting/ScriptingHost.h"
|
||||
#include "simulation2/Simulation2.h"
|
||||
#include "sound/CMusicPlayer.h"
|
||||
#include "gui/GUIManager.h"
|
||||
@ -228,7 +230,7 @@ static void Frame()
|
||||
// If we are not running a multiplayer game, disable updates when the game is
|
||||
// minimized or out of focus and relinquish the CPU a bit, in order to make
|
||||
// debugging easier.
|
||||
if( !g_NetClient && !g_NetServer && !g_app_has_focus )
|
||||
if( !g_NetClient && !g_app_has_focus )
|
||||
{
|
||||
need_update = false;
|
||||
// don't use SDL_WaitEvent: don't want the main loop to freeze until app focus is restored
|
||||
|
@ -1,151 +0,0 @@
|
||||
/* Copyright (C) 2009 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 0 A.D. is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef INCLUDED_NETWORK_JSEVENTS
|
||||
#define INCLUDED_NETWORK_JSEVENTS
|
||||
|
||||
#include "ServerSession.h"
|
||||
#include "scripting/DOMEvent.h"
|
||||
|
||||
enum ENetworkJSEvents
|
||||
{
|
||||
NET_JS_EVENT_START_GAME,
|
||||
NET_JS_EVENT_CHAT,
|
||||
NET_JS_EVENT_CONNECT_COMPLETE,
|
||||
NET_JS_EVENT_DISCONNECT,
|
||||
NET_JS_EVENT_CLIENT_CONNECT,
|
||||
NET_JS_EVENT_CLIENT_DISCONNECT,
|
||||
NET_JS_EVENT_LAST
|
||||
};
|
||||
|
||||
class CStartGameEvent: public CScriptEvent
|
||||
{
|
||||
public:
|
||||
CStartGameEvent():
|
||||
CScriptEvent(L"startGame", NET_JS_EVENT_START_GAME, false)
|
||||
{}
|
||||
};
|
||||
|
||||
class CChatEvent: public CScriptEvent
|
||||
{
|
||||
CStrW m_Sender;
|
||||
CStrW m_Message;
|
||||
|
||||
public:
|
||||
CChatEvent(const CStrW& sender, const CStrW& message):
|
||||
CScriptEvent(L"chat", NET_JS_EVENT_CHAT, false ),
|
||||
m_Sender(sender),
|
||||
m_Message(message)
|
||||
{
|
||||
AddLocalProperty(L"sender", &m_Sender, true);
|
||||
AddLocalProperty(L"message", &m_Message, true);
|
||||
}
|
||||
};
|
||||
|
||||
class CConnectCompleteEvent: public CScriptEvent
|
||||
{
|
||||
CStrW m_Message;
|
||||
bool m_Success;
|
||||
|
||||
public:
|
||||
CConnectCompleteEvent(const CStrW& message, bool success):
|
||||
CScriptEvent(L"connectComplete", NET_JS_EVENT_CONNECT_COMPLETE, false),
|
||||
m_Message(message),
|
||||
m_Success(success)
|
||||
{
|
||||
AddLocalProperty(L"message", &m_Message, true);
|
||||
AddLocalProperty(L"success", &m_Success, true);
|
||||
}
|
||||
};
|
||||
|
||||
class CDisconnectEvent: public CScriptEvent
|
||||
{
|
||||
CStrW m_Message;
|
||||
|
||||
public:
|
||||
CDisconnectEvent(const CStrW& message):
|
||||
CScriptEvent(L"disconnect", NET_JS_EVENT_DISCONNECT, false),
|
||||
m_Message(message)
|
||||
{
|
||||
AddLocalProperty(L"message", &m_Message, true);
|
||||
}
|
||||
};
|
||||
|
||||
class CClientConnectDisconnectCommon: public CScriptEvent
|
||||
{
|
||||
int m_SessionID;
|
||||
CStrW m_Name;
|
||||
CNetServerSession *m_pSession;
|
||||
|
||||
public:
|
||||
CClientConnectDisconnectCommon(const wchar_t* UNUSED(eventName), int UNUSED(eventType),
|
||||
int sessionID, const CStrW& name, CNetServerSession* pSession)
|
||||
: CScriptEvent(L"clientConnect", NET_JS_EVENT_CLIENT_CONNECT, false),
|
||||
m_SessionID(sessionID),
|
||||
m_Name(name),
|
||||
m_pSession(pSession)
|
||||
{
|
||||
AddLocalProperty(L"id", &m_SessionID, true);
|
||||
AddLocalProperty(L"name", &m_Name, true);
|
||||
if (m_pSession)
|
||||
AddLocalProperty(L"session", &m_pSession, true);
|
||||
}
|
||||
};
|
||||
|
||||
struct CClientConnectEvent: public CClientConnectDisconnectCommon
|
||||
{
|
||||
CClientConnectEvent(int sessionID, const CStrW& name):
|
||||
CClientConnectDisconnectCommon(
|
||||
L"clientConnect",
|
||||
NET_JS_EVENT_CLIENT_CONNECT,
|
||||
sessionID,
|
||||
name,
|
||||
NULL)
|
||||
{}
|
||||
|
||||
CClientConnectEvent(CNetServerSession *pSession):
|
||||
CClientConnectDisconnectCommon(
|
||||
L"clientConnect",
|
||||
NET_JS_EVENT_CLIENT_CONNECT,
|
||||
pSession->GetID(),
|
||||
pSession->GetName(),
|
||||
pSession)
|
||||
{}
|
||||
};
|
||||
|
||||
struct CClientDisconnectEvent: public CClientConnectDisconnectCommon
|
||||
{
|
||||
CClientDisconnectEvent(int sessionID, const CStrW& name):
|
||||
CClientConnectDisconnectCommon(
|
||||
L"clientDisconnect",
|
||||
NET_JS_EVENT_CLIENT_DISCONNECT,
|
||||
sessionID,
|
||||
name,
|
||||
NULL)
|
||||
{}
|
||||
|
||||
CClientDisconnectEvent(CNetServerSession *pSession):
|
||||
CClientConnectDisconnectCommon(
|
||||
L"clientDisconnect",
|
||||
NET_JS_EVENT_CLIENT_DISCONNECT,
|
||||
pSession->GetID(),
|
||||
pSession->GetName(),
|
||||
pSession)
|
||||
{}
|
||||
};
|
||||
|
||||
#endif
|
@ -15,623 +15,380 @@
|
||||
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
*-----------------------------------------------------------------------------
|
||||
* FILE : NetClient.cpp
|
||||
* PROJECT : 0 A.D.
|
||||
* DESCRIPTION : Network client class implementation file
|
||||
*-----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
// INCLUDES
|
||||
#include "precompiled.h"
|
||||
|
||||
#include "NetClient.h"
|
||||
|
||||
#include "NetJsEvents.h"
|
||||
#include "Network.h"
|
||||
#include "NetServer.h"
|
||||
#include "NetMessage.h"
|
||||
#include "NetSession.h"
|
||||
#include "NetTurnManager.h"
|
||||
|
||||
#include "scripting/DOMEvent.h"
|
||||
#include "scripting/JSConversions.h"
|
||||
#include "scripting/ScriptableObject.h"
|
||||
#include "ps/CConsole.h"
|
||||
#include "ps/CLogger.h"
|
||||
#include "ps/CStr.h"
|
||||
#include "ps/Game.h"
|
||||
#include "ps/Globals.h"
|
||||
#include "ps/GameAttributes.h"
|
||||
#include "scriptinterface/ScriptInterface.h"
|
||||
#include "simulation2/Simulation2.h"
|
||||
|
||||
// DECLARATIONS
|
||||
|
||||
#define LOG_CATEGORY L"net"
|
||||
#include <boost/nondet_random.hpp>
|
||||
#include <boost/random.hpp>
|
||||
|
||||
CNetClient *g_NetClient = NULL;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: CServerPlayer()
|
||||
// Desc: Constructor
|
||||
//-----------------------------------------------------------------------------
|
||||
CServerPlayer::CServerPlayer( uint sessionID, const CStr& nickname )
|
||||
CNetClient::CNetClient(CGame* game) :
|
||||
m_Session(NULL),
|
||||
m_UserName(L"anonymous"),
|
||||
m_GUID(GenerateGUID()), m_HostID((u32)-1), m_ClientTurnManager(NULL), m_Game(game)
|
||||
{
|
||||
m_SessionID = sessionID;
|
||||
m_Nickname = nickname;
|
||||
}
|
||||
m_Game->SetTurnManager(NULL); // delete the old local turn manager so we don't accidentally use it
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: ~CServerPlayer()
|
||||
// Desc: Destructor
|
||||
//-----------------------------------------------------------------------------
|
||||
CServerPlayer::~CServerPlayer( void )
|
||||
{
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: ScriptingInit()
|
||||
// Desc:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CServerPlayer::ScriptingInit( void )
|
||||
{
|
||||
AddProperty(L"id", &CServerPlayer::m_SessionID, true);
|
||||
AddProperty(L"name", &CServerPlayer::m_Nickname, true);
|
||||
|
||||
CJSObject<CServerPlayer>::ScriptingInit( "NetClient_ServerSession" );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: CNetClient()
|
||||
// Desc: Constructor
|
||||
//-----------------------------------------------------------------------------
|
||||
CNetClient::CNetClient( ScriptInterface& scriptInterface, CGame* pGame, CGameAttributes* pGameAttribs )
|
||||
: CNetHost( scriptInterface ), m_JsPlayers( &m_Players )
|
||||
{
|
||||
m_ClientTurnManager = NULL;
|
||||
|
||||
m_pLocalPlayerSlot = NULL;
|
||||
m_pGame = pGame;
|
||||
m_pGameAttributes = pGameAttribs;
|
||||
|
||||
g_ScriptingHost.SetGlobal("g_NetClient", OBJECT_TO_JSVAL(GetScript()));
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: ~CNetClient()
|
||||
// Desc: Destructor
|
||||
//-----------------------------------------------------------------------------
|
||||
CNetClient::~CNetClient()
|
||||
{
|
||||
// Release resources
|
||||
PlayerMap::iterator it = m_Players.begin();
|
||||
for ( ; it != m_Players.end(); it++ )
|
||||
{
|
||||
CServerPlayer *pCurrPlayer = it->second;
|
||||
if ( pCurrPlayer ) delete pCurrPlayer;
|
||||
}
|
||||
|
||||
m_Players.clear();
|
||||
|
||||
g_ScriptingHost.SetGlobal("g_NetClient", JSVAL_NULL);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: ScriptingInit()
|
||||
// Desc:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CNetClient::ScriptingInit()
|
||||
{
|
||||
AddMethod<bool, &CNetClient::SetupConnection>("beginConnect", 1);
|
||||
|
||||
AddProperty(L"onStartGame", &CNetClient::m_OnStartGame);
|
||||
AddProperty(L"onChat", &CNetClient::m_OnChat);
|
||||
AddProperty(L"onConnectComplete", &CNetClient::m_OnConnectComplete);
|
||||
AddProperty(L"onDisconnect", &CNetClient::m_OnDisconnect);
|
||||
AddProperty(L"onClientConnect", &CNetClient::m_OnPlayerJoin);
|
||||
AddProperty(L"onClientDisconnect", &CNetClient::m_OnPlayerLeave);
|
||||
|
||||
AddProperty(L"password", &CNetClient::m_Password);
|
||||
AddProperty<CStr>(L"playerName", &CNetClient::m_Nickname);
|
||||
|
||||
AddProperty(L"sessions", &CNetClient::m_JsPlayers);
|
||||
|
||||
CJSMap< PlayerMap >::ScriptingInit("NetClient_SessionMap");
|
||||
CJSObject<CNetClient>::ScriptingInit("NetClient");
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: Run()
|
||||
// Desc: Connect to server and start main loop
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CNetClient::SetupConnection( JSContext* UNUSED(pContext), uintN argc, jsval* argv )
|
||||
{
|
||||
uint port = DEFAULT_HOST_PORT;
|
||||
|
||||
// Validate parameters
|
||||
if ( argc == 0 ) return false;
|
||||
|
||||
// Build host information
|
||||
CStr host = g_ScriptingHost.ValueToString( argv[0] );
|
||||
if ( argc == 2 ) port = ToPrimitive< uint >( argv[ 1 ] );
|
||||
|
||||
// Create client host
|
||||
if ( !Create() ) return false;
|
||||
|
||||
// Connect to server
|
||||
return Connect( host, port );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: SetupSession()
|
||||
// Desc: Setup client session upon creation
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CNetClient::SetupSession( CNetSession* pSession )
|
||||
{
|
||||
// Validate parameters
|
||||
if ( !pSession ) return false;
|
||||
|
||||
FsmActionCtx* pContext = pSession->GetFsmActionCtx();
|
||||
|
||||
pContext->pHost = this;
|
||||
pContext->pSession = pSession;
|
||||
void* context = this;
|
||||
|
||||
// Set up transitions for session
|
||||
pSession->AddTransition( NCS_CONNECT, ( uint )NMT_SERVER_HANDSHAKE, NCS_HANDSHAKE, (void*)&OnHandshake, pContext );
|
||||
AddTransition(NCS_UNCONNECTED, (uint)NMT_CONNECT_COMPLETE, NCS_CONNECT, (void*)&OnConnect, context);
|
||||
|
||||
pSession->AddTransition( NCS_HANDSHAKE, ( uint )NMT_ERROR, NCS_CONNECT, (void*)&OnError, pContext );
|
||||
pSession->AddTransition( NCS_HANDSHAKE, ( uint )NMT_SERVER_HANDSHAKE_RESPONSE, NCS_AUTHENTICATE, (void*)&OnHandshake, pContext );
|
||||
AddTransition(NCS_CONNECT, (uint)NMT_SERVER_HANDSHAKE, NCS_HANDSHAKE, (void*)&OnHandshake, context);
|
||||
|
||||
pSession->AddTransition( NCS_AUTHENTICATE, ( uint )NMT_ERROR, NCS_CONNECT, (void*)&OnError, pContext );
|
||||
pSession->AddTransition( NCS_AUTHENTICATE, ( uint )NMT_AUTHENTICATE_RESULT, NCS_PREGAME, (void*)&OnAuthenticate, pContext );
|
||||
AddTransition(NCS_HANDSHAKE, (uint)NMT_SERVER_HANDSHAKE_RESPONSE, NCS_AUTHENTICATE, (void*)&OnHandshakeResponse, context);
|
||||
|
||||
pSession->AddTransition( NCS_PREGAME, ( uint )NMT_ERROR, NCS_CONNECT, (void*)&OnError, pContext );
|
||||
pSession->AddTransition( NCS_PREGAME, ( uint )NMT_GAME_SETUP, NCS_PREGAME, (void*)&OnPreGame, pContext );
|
||||
pSession->AddTransition( NCS_PREGAME, ( uint )NMT_ASSIGN_PLAYER_SLOT, NCS_PREGAME, (void*)&OnPreGame, pContext );
|
||||
pSession->AddTransition( NCS_PREGAME, ( uint )NMT_PLAYER_CONFIG, NCS_PREGAME, (void*)&OnPreGame, pContext );
|
||||
pSession->AddTransition( NCS_PREGAME, ( uint )NMT_PLAYER_JOIN, NCS_PREGAME, (void*)&OnPlayerJoin, pContext );
|
||||
pSession->AddTransition( NCS_PREGAME, ( uint )NMT_GAME_START, NCS_INGAME, (void*)&OnStartGame_, pContext );
|
||||
AddTransition(NCS_AUTHENTICATE, (uint)NMT_AUTHENTICATE_RESULT, NCS_INITIAL_GAMESETUP, (void*)&OnAuthenticate, context);
|
||||
|
||||
pSession->AddTransition( NCS_INGAME, ( uint )NMT_CHAT, NCS_INGAME, (void*)&OnChat, pContext );
|
||||
pSession->AddTransition( NCS_INGAME, ( uint )NMT_SIMULATION_COMMAND, NCS_INGAME, (void*)&OnInGame, pContext );
|
||||
pSession->AddTransition( NCS_INGAME, ( uint )NMT_SYNC_ERROR, NCS_INGAME, (void*)&OnInGame, pContext );
|
||||
pSession->AddTransition( NCS_INGAME, ( uint )NMT_END_COMMAND_BATCH, NCS_INGAME, (void*)&OnInGame, pContext );
|
||||
AddTransition(NCS_INITIAL_GAMESETUP, (uint)NMT_GAME_SETUP, NCS_PREGAME, (void*)&OnGameSetup, context);
|
||||
|
||||
AddTransition(NCS_PREGAME, (uint)NMT_GAME_SETUP, NCS_PREGAME, (void*)&OnGameSetup, context);
|
||||
AddTransition(NCS_PREGAME, (uint)NMT_PLAYER_ASSIGNMENT, NCS_PREGAME, (void*)&OnPlayerAssignment, context);
|
||||
AddTransition(NCS_PREGAME, (uint)NMT_GAME_START, NCS_LOADING, (void*)&OnGameStart, context);
|
||||
|
||||
AddTransition(NCS_LOADING, (uint)NMT_GAME_SETUP, NCS_LOADING, (void*)&OnGameSetup, context);
|
||||
AddTransition(NCS_LOADING, (uint)NMT_PLAYER_ASSIGNMENT, NCS_LOADING, (void*)&OnPlayerAssignment, context);
|
||||
AddTransition(NCS_LOADING, (uint)NMT_LOADED_GAME, NCS_INGAME, (void*)&OnLoadedGame, context);
|
||||
|
||||
AddTransition(NCS_INGAME, (uint)NMT_GAME_SETUP, NCS_INGAME, (void*)&OnGameSetup, context);
|
||||
AddTransition(NCS_INGAME, (uint)NMT_PLAYER_ASSIGNMENT, NCS_INGAME, (void*)&OnPlayerAssignment, context);
|
||||
AddTransition(NCS_INGAME, (uint)NMT_SIMULATION_COMMAND, NCS_INGAME, (void*)&OnInGame, context);
|
||||
AddTransition(NCS_INGAME, (uint)NMT_SYNC_ERROR, NCS_INGAME, (void*)&OnInGame, context);
|
||||
AddTransition(NCS_INGAME, (uint)NMT_END_COMMAND_BATCH, NCS_INGAME, (void*)&OnInGame, context);
|
||||
|
||||
// TODO: add chat
|
||||
|
||||
// Set first state
|
||||
pSession->SetFirstState( NCS_CONNECT );
|
||||
SetFirstState(NCS_UNCONNECTED);
|
||||
}
|
||||
|
||||
CNetClient::~CNetClient()
|
||||
{
|
||||
delete m_Session;
|
||||
}
|
||||
|
||||
void CNetClient::SetUserName(const CStrW& username)
|
||||
{
|
||||
debug_assert(!m_Session); // must be called before we start the connection
|
||||
|
||||
m_UserName = username;
|
||||
}
|
||||
|
||||
bool CNetClient::SetupConnection(const CStr& server)
|
||||
{
|
||||
CNetClientSessionRemote* session = new CNetClientSessionRemote(*this);
|
||||
bool ok = session->Connect(PS_DEFAULT_PORT, server);
|
||||
SetAndOwnSession(session);
|
||||
return ok;
|
||||
}
|
||||
|
||||
void CNetClient::SetupLocalConnection(CNetServer& server)
|
||||
{
|
||||
CNetClientSessionLocal* session = new CNetClientSessionLocal(*this, server);
|
||||
SetAndOwnSession(session);
|
||||
}
|
||||
|
||||
void CNetClient::SetAndOwnSession(CNetClientSession* session)
|
||||
{
|
||||
delete m_Session;
|
||||
m_Session = session;
|
||||
}
|
||||
|
||||
void CNetClient::Poll()
|
||||
{
|
||||
if (m_Session)
|
||||
m_Session->Poll();
|
||||
}
|
||||
|
||||
CScriptValRooted CNetClient::GuiPoll()
|
||||
{
|
||||
if (m_GuiMessageQueue.empty())
|
||||
return CScriptValRooted();
|
||||
|
||||
CScriptValRooted r = m_GuiMessageQueue.front();
|
||||
m_GuiMessageQueue.pop_front();
|
||||
return r;
|
||||
}
|
||||
|
||||
void CNetClient::PushGuiMessage(const CScriptValRooted& message)
|
||||
{
|
||||
debug_assert(!message.undefined());
|
||||
|
||||
m_GuiMessageQueue.push_back(message);
|
||||
}
|
||||
|
||||
std::wstring CNetClient::TestReadGuiMessages()
|
||||
{
|
||||
std::wstring r;
|
||||
while (true)
|
||||
{
|
||||
CScriptValRooted msg = GuiPoll();
|
||||
if (msg.undefined())
|
||||
break;
|
||||
r += GetScriptInterface().ToString(msg.get()) + L"\n";
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
ScriptInterface& CNetClient::GetScriptInterface()
|
||||
{
|
||||
return m_Game->GetSimulation2()->GetScriptInterface();
|
||||
}
|
||||
|
||||
void CNetClient::PostPlayerAssignmentsToScript()
|
||||
{
|
||||
CScriptValRooted msg;
|
||||
GetScriptInterface().Eval("({'type':'players', 'hosts':{}})", msg);
|
||||
|
||||
CScriptValRooted hosts;
|
||||
GetScriptInterface().GetProperty(msg.get(), "hosts", hosts);
|
||||
|
||||
for (PlayerAssignmentMap::iterator it = m_PlayerAssignments.begin(); it != m_PlayerAssignments.end(); ++it)
|
||||
{
|
||||
CScriptValRooted host;
|
||||
GetScriptInterface().Eval("({})", host);
|
||||
GetScriptInterface().SetProperty(host.get(), "name", std::wstring(it->second.m_Name), false);
|
||||
GetScriptInterface().SetProperty(host.get(), "player", it->second.m_PlayerID, false);
|
||||
GetScriptInterface().SetProperty(hosts.get(), it->first, host, false);
|
||||
}
|
||||
|
||||
PushGuiMessage(msg);
|
||||
}
|
||||
|
||||
bool CNetClient::SendMessage(const CNetMessage* message)
|
||||
{
|
||||
return m_Session->SendMessage(message);
|
||||
}
|
||||
|
||||
void CNetClient::HandleConnect()
|
||||
{
|
||||
Update((uint)NMT_CONNECT_COMPLETE, NULL);
|
||||
}
|
||||
|
||||
void CNetClient::HandleDisconnect()
|
||||
{
|
||||
// TODO: should do something
|
||||
}
|
||||
|
||||
bool CNetClient::HandleMessage(CNetMessage* message)
|
||||
{
|
||||
// Update FSM
|
||||
bool ok = Update(message->GetType(), message);
|
||||
if (!ok)
|
||||
LOGERROR(L"Net client: Error running FSM update (type=%d state=%d)", (int)message->GetType(), (int)GetCurrState());
|
||||
return ok;
|
||||
}
|
||||
|
||||
void CNetClient::LoadFinished()
|
||||
{
|
||||
m_Game->ChangeNetStatus(CGame::NET_WAITING_FOR_CONNECT);
|
||||
|
||||
CLoadedGameMessage loaded;
|
||||
SendMessage(&loaded);
|
||||
}
|
||||
|
||||
bool CNetClient::OnConnect(void* context, CFsmEvent* event)
|
||||
{
|
||||
debug_assert(event->GetType() == (uint)NMT_CONNECT_COMPLETE);
|
||||
|
||||
CNetClient* client = (CNetClient*)context;
|
||||
|
||||
CScriptValRooted msg;
|
||||
client->GetScriptInterface().Eval("({'type':'netstatus','status':'connected'})", msg);
|
||||
client->PushGuiMessage(msg);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: HandleConnect()
|
||||
// Desc: Called when the client successfully connected to server
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CNetClient::HandleConnect( CNetSession* pSession )
|
||||
bool CNetClient::OnHandshake(void* context, CFsmEvent* event)
|
||||
{
|
||||
// Validate parameters
|
||||
if ( !pSession ) return false;
|
||||
debug_assert(event->GetType() == (uint)NMT_SERVER_HANDSHAKE);
|
||||
|
||||
return true;
|
||||
}
|
||||
CNetClient* client = (CNetClient*)context;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: HandleDisconnect()
|
||||
// Desc: Called when the client disconnected from the server
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CNetClient::HandleDisconnect( CNetSession *pSession )
|
||||
{
|
||||
// Validate parameters
|
||||
if ( !pSession ) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: OnError()
|
||||
// Desc:
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CNetClient::OnError( void* pContext, CFsmEvent* pEvent )
|
||||
{
|
||||
// Validate parameters
|
||||
if ( !pEvent || !pContext ) return false;
|
||||
|
||||
// Error event?
|
||||
if ( pEvent->GetType() != (uint)NMT_ERROR ) return true;
|
||||
|
||||
CNetClient* pClient = ( CNetClient* )( ( FsmActionCtx* )pContext )->pHost;
|
||||
debug_assert( pClient );
|
||||
|
||||
CErrorMessage* pMessage = ( CErrorMessage* )pEvent->GetParamRef();
|
||||
if ( pMessage )
|
||||
{
|
||||
LOG( CLogger::Error, LOG_CATEGORY, L"CNetClient::OnError(): Error description %hs", pMessage->m_Error );
|
||||
|
||||
if ( pClient->m_OnConnectComplete.Defined() )
|
||||
{
|
||||
CConnectCompleteEvent connectComplete( ( CStrW )pMessage->m_Error, false );
|
||||
pClient->m_OnConnectComplete.DispatchEvent( pClient->GetScript(), &connectComplete );
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: OnPlayer()
|
||||
// Desc:
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CNetClient::OnPlayerJoin( void* pContext, CFsmEvent* pEvent )
|
||||
{
|
||||
// Validate parameters
|
||||
if ( !pEvent || !pContext ) return false;
|
||||
|
||||
// Connect event?
|
||||
if ( pEvent->GetType() != NMT_PLAYER_JOIN ) return true;
|
||||
|
||||
CNetClient* pClient = ( CNetClient* )( ( FsmActionCtx* )pContext )->pHost;
|
||||
debug_assert( pClient );
|
||||
|
||||
CPlayerJoinMessage* pMessage = ( CPlayerJoinMessage* )pEvent->GetParamRef();
|
||||
if ( pMessage )
|
||||
{
|
||||
for ( uint i = 0; i < pMessage->m_Clients.size(); i++ )
|
||||
{
|
||||
pClient->OnPlayer( pMessage->m_Clients[ i ].m_SessionID, pMessage->m_Clients[ i ].m_Name );
|
||||
}
|
||||
|
||||
pClient->OnConnectComplete();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: OnConnectComplete()
|
||||
// Desc:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CNetClient::OnConnectComplete( )
|
||||
{
|
||||
if ( m_OnConnectComplete.Defined() )
|
||||
{
|
||||
CConnectCompleteEvent connectComplete( "OK", true );
|
||||
m_OnConnectComplete.DispatchEvent( GetScript(), &connectComplete );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: OnHandshake()
|
||||
// Desc:
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CNetClient::OnHandshake( void* pContext, CFsmEvent* pEvent )
|
||||
{
|
||||
// Validate parameters
|
||||
if ( !pEvent || !pContext ) return false;
|
||||
|
||||
CNetClient* pClient = ( CNetClient* )( ( FsmActionCtx* )pContext )->pHost;
|
||||
CNetSession* pSession = ( ( FsmActionCtx* )pContext )->pSession;
|
||||
|
||||
debug_assert( pClient );
|
||||
debug_assert( pSession );
|
||||
|
||||
switch ( pEvent->GetType() )
|
||||
{
|
||||
//case NMT_ERROR:
|
||||
|
||||
// CNetClient::OnError( pContext, pEvent );
|
||||
// break;
|
||||
|
||||
case NMT_SERVER_HANDSHAKE:
|
||||
{
|
||||
CCliHandshakeMessage handshake;
|
||||
handshake.m_MagicResponse = PS_PROTOCOL_MAGIC_RESPONSE;
|
||||
handshake.m_ProtocolVersion = PS_PROTOCOL_VERSION;
|
||||
handshake.m_SoftwareVersion = PS_PROTOCOL_VERSION;
|
||||
pClient->SendMessage( pSession, &handshake );
|
||||
}
|
||||
break;
|
||||
client->SendMessage(&handshake);
|
||||
|
||||
case NMT_SERVER_HANDSHAKE_RESPONSE:
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CNetClient::OnHandshakeResponse(void* context, CFsmEvent* event)
|
||||
{
|
||||
debug_assert(event->GetType() == (uint)NMT_SERVER_HANDSHAKE_RESPONSE);
|
||||
|
||||
CNetClient* client = (CNetClient*)context;
|
||||
|
||||
CAuthenticateMessage authenticate;
|
||||
authenticate.m_Name = pClient->m_Nickname;
|
||||
authenticate.m_Password = pClient->m_Password;
|
||||
pClient->SendMessage( pSession, &authenticate );
|
||||
}
|
||||
break;
|
||||
}
|
||||
authenticate.m_GUID = client->m_GUID;
|
||||
authenticate.m_Name = client->m_UserName;
|
||||
authenticate.m_Password = L""; // TODO
|
||||
client->SendMessage(&authenticate);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: OnAuthenticate()
|
||||
// Desc:
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CNetClient::OnAuthenticate( void* pContext, CFsmEvent* pEvent )
|
||||
bool CNetClient::OnAuthenticate(void* context, CFsmEvent* event)
|
||||
{
|
||||
// Validate parameters
|
||||
if ( !pEvent || !pContext ) return false;
|
||||
debug_assert(event->GetType() == (uint)NMT_AUTHENTICATE_RESULT);
|
||||
|
||||
CNetClient* pClient = ( CNetClient* )( ( FsmActionCtx* )pContext )->pHost;
|
||||
UNUSED2(pClient);
|
||||
CNetSession* pSession = ( ( FsmActionCtx* )pContext )->pSession;
|
||||
CNetClient* client = (CNetClient*)context;
|
||||
|
||||
debug_assert( pClient );
|
||||
debug_assert( pSession );
|
||||
CAuthenticateResultMessage* message = (CAuthenticateResultMessage*)event->GetParamRef();
|
||||
|
||||
if ( pEvent->GetType() == (uint)NMT_ERROR )
|
||||
{
|
||||
// return CNetClient::OnError( pContext, pEvent );
|
||||
}
|
||||
else if ( pEvent->GetType() == NMT_AUTHENTICATE_RESULT )
|
||||
{
|
||||
CAuthenticateResultMessage* pMessage =( CAuthenticateResultMessage* )pEvent->GetParamRef();
|
||||
if ( !pMessage ) return true;
|
||||
LOGMESSAGE(L"Net: Authentication result: host=%d, %ls", message->m_HostID, message->m_Message.c_str() );
|
||||
|
||||
LOG(CLogger::Error, LOG_CATEGORY, L"CNetClient::OnAuthenticate(): Authentication result: %ls", pMessage->m_Message.c_str() );
|
||||
client->m_HostID = message->m_HostID;
|
||||
|
||||
pSession->SetID( pMessage->m_SessionID );
|
||||
|
||||
LOG(CLogger::Error, LOG_CATEGORY, L"CNetClient::OnAuthenticate(): My session ID is %d", pMessage->m_SessionID);
|
||||
}
|
||||
CScriptValRooted msg;
|
||||
client->GetScriptInterface().Eval("({'type':'netstatus','status':'authenticated'})", msg);
|
||||
client->PushGuiMessage(msg);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: OnPreGame()
|
||||
// Desc:
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CNetClient::OnPreGame( void* pContext, CFsmEvent* pEvent )
|
||||
bool CNetClient::OnGameSetup(void* context, CFsmEvent* event)
|
||||
{
|
||||
// Validate parameters
|
||||
if ( !pEvent || !pContext ) return false;
|
||||
debug_assert(event->GetType() == (uint)NMT_GAME_SETUP);
|
||||
|
||||
CNetClient* pClient = ( CNetClient* )( ( FsmActionCtx* )pContext )->pHost;
|
||||
CNetSession* pSession = ( CNetSession* )( ( FsmActionCtx* )pContext )->pSession;
|
||||
CNetClient* client = (CNetClient*)context;
|
||||
|
||||
debug_assert( pClient );
|
||||
debug_assert( pSession );
|
||||
CGameSetupMessage* message = (CGameSetupMessage*)event->GetParamRef();
|
||||
|
||||
switch ( pEvent->GetType() )
|
||||
{
|
||||
case NMT_PLAYER_LEAVE:
|
||||
{
|
||||
CPlayerLeaveMessage* pMessage = ( CPlayerLeaveMessage* )pEvent->GetParamRef();
|
||||
if ( !pMessage ) return false;
|
||||
client->m_GameAttributes = message->m_Data;
|
||||
|
||||
pClient->OnPlayerLeave( pMessage->m_SessionID );
|
||||
}
|
||||
break;
|
||||
|
||||
case NMT_GAME_SETUP:
|
||||
{
|
||||
CGameSetupMessage* pMessage = ( CGameSetupMessage* )pEvent->GetParamRef();
|
||||
|
||||
for ( uint i = 0; i < pMessage->m_Values.size(); i++ )
|
||||
{
|
||||
pClient->m_pGameAttributes->SetValue( pMessage->m_Values[ i ].m_Name, pMessage->m_Values[ i ].m_Value );
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case NMT_ASSIGN_PLAYER_SLOT:
|
||||
{
|
||||
CAssignPlayerSlotMessage* pMessage = ( CAssignPlayerSlotMessage* )pEvent->GetParamRef();
|
||||
|
||||
// FIXME Validate slot id to prevent us from going boom
|
||||
CPlayerSlot* pSlot = pClient->m_pGameAttributes->GetSlot( pMessage->m_SlotID );
|
||||
if ( pSlot == pClient->m_pLocalPlayerSlot ) {
|
||||
pClient->m_pLocalPlayerSlot = NULL;
|
||||
}
|
||||
|
||||
switch ( pMessage->m_Assignment )
|
||||
{
|
||||
case ASSIGN_SESSION:
|
||||
{
|
||||
// TODO: Check where is the best place to assign client's session ID
|
||||
if ( pSession->GetID() == pMessage->m_SessionID )
|
||||
{
|
||||
pClient->m_pLocalPlayerSlot = pSlot;
|
||||
}
|
||||
pSlot->AssignToSessionID( pMessage->m_SessionID );
|
||||
}
|
||||
break;
|
||||
|
||||
case ASSIGN_CLOSED:
|
||||
pSlot->AssignClosed();
|
||||
break;
|
||||
|
||||
case ASSIGN_OPEN:
|
||||
pSlot->AssignOpen();
|
||||
break;
|
||||
|
||||
default:
|
||||
LOG( CLogger::Warning, LOG_CATEGORY, L"Invalid slot assignment %hs", pMessage->ToString().c_str() );
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case NMT_PLAYER_CONFIG:
|
||||
{
|
||||
CPlayerConfigMessage* pMessage = ( CPlayerConfigMessage* )pEvent->GetParamRef();
|
||||
|
||||
// FIXME Check player ID
|
||||
CPlayer* pPlayer = pClient->m_pGameAttributes->GetPlayer( pMessage->m_PlayerID );
|
||||
|
||||
for ( uint i = 0; i < pMessage->m_Values.size(); i++ )
|
||||
{
|
||||
pPlayer->SetValue( pMessage->m_Values[i].m_Name, pMessage->m_Values[i].m_Value );
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
CScriptValRooted msg;
|
||||
client->GetScriptInterface().Eval("({'type':'gamesetup'})", msg);
|
||||
client->GetScriptInterface().SetProperty(msg.get(), "data", message->m_Data, false);
|
||||
client->PushGuiMessage(msg);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: OnInGame()
|
||||
// Desc:
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CNetClient::OnInGame( void *pContext, CFsmEvent* pEvent )
|
||||
bool CNetClient::OnPlayerAssignment(void* context, CFsmEvent* event)
|
||||
{
|
||||
// Validate parameters
|
||||
if ( !pEvent || !pContext ) return false;
|
||||
debug_assert(event->GetType() == (uint)NMT_PLAYER_ASSIGNMENT);
|
||||
|
||||
CNetClient* pClient = ( CNetClient* )( ( FsmActionCtx* )pContext )->pHost;
|
||||
CNetClient* client = (CNetClient*)context;
|
||||
|
||||
CNetMessage* pMessage = ( CNetMessage* )pEvent->GetParamRef();
|
||||
if ( pMessage )
|
||||
CPlayerAssignmentMessage* message = (CPlayerAssignmentMessage*)event->GetParamRef();
|
||||
|
||||
// Unpack the message
|
||||
client->m_PlayerAssignments.clear();
|
||||
for (size_t i = 0; i < message->m_Hosts.size(); ++i)
|
||||
{
|
||||
if (pMessage->GetType() == NMT_SIMULATION_COMMAND)
|
||||
{
|
||||
CSimulationMessage* simMessage = static_cast<CSimulationMessage*> (pMessage);
|
||||
pClient->m_ClientTurnManager->OnSimulationMessage(simMessage);
|
||||
PlayerAssignment assignment;
|
||||
assignment.m_Name = message->m_Hosts[i].m_Name;
|
||||
assignment.m_PlayerID = message->m_Hosts[i].m_PlayerID;
|
||||
client->m_PlayerAssignments[message->m_Hosts[i].m_GUID] = assignment;
|
||||
}
|
||||
else if (pMessage->GetType() == NMT_SYNC_ERROR)
|
||||
{
|
||||
CSyncErrorMessage* syncMessage = static_cast<CSyncErrorMessage*> (pMessage);
|
||||
pClient->m_ClientTurnManager->OnSyncError(syncMessage->m_Turn, syncMessage->m_HashExpected);
|
||||
}
|
||||
else if ( pMessage->GetType() == NMT_END_COMMAND_BATCH )
|
||||
{
|
||||
CEndCommandBatchMessage* pMessage = ( CEndCommandBatchMessage* )pEvent->GetParamRef();
|
||||
if ( !pMessage ) return false;
|
||||
|
||||
CEndCommandBatchMessage* endMessage = static_cast<CEndCommandBatchMessage*> (pMessage);
|
||||
pClient->m_ClientTurnManager->FinishedAllCommands(endMessage->m_Turn);
|
||||
client->PostPlayerAssignmentsToScript();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CNetClient::OnGameStart(void* context, CFsmEvent* event)
|
||||
{
|
||||
debug_assert(event->GetType() == (uint)NMT_GAME_START);
|
||||
|
||||
CNetClient* client = (CNetClient*)context;
|
||||
|
||||
// Find the player assigned to our GUID
|
||||
int player = -1;
|
||||
if (client->m_PlayerAssignments.find(client->m_GUID) != client->m_PlayerAssignments.end())
|
||||
player = client->m_PlayerAssignments[client->m_GUID].m_PlayerID;
|
||||
|
||||
client->m_ClientTurnManager = new CNetClientTurnManager(*client->m_Game->GetSimulation2(), *client, client->m_HostID);
|
||||
|
||||
client->m_Game->SetPlayerID(player);
|
||||
client->m_Game->StartGame(client->m_GameAttributes);
|
||||
|
||||
CScriptValRooted msg;
|
||||
client->GetScriptInterface().Eval("({'type':'start'})", msg);
|
||||
client->PushGuiMessage(msg);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CNetClient::OnLoadedGame(void* context, CFsmEvent* event)
|
||||
{
|
||||
debug_assert(event->GetType() == (uint)NMT_LOADED_GAME);
|
||||
|
||||
CNetClient* client = (CNetClient*)context;
|
||||
|
||||
// All players have loaded the game - start running the turn manager
|
||||
// so that the game begins
|
||||
client->m_Game->SetTurnManager(client->m_ClientTurnManager);
|
||||
client->m_Game->ChangeNetStatus(CGame::NET_NORMAL);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CNetClient::OnInGame(void *context, CFsmEvent* event)
|
||||
{
|
||||
// TODO: should split each of these cases into a separate method
|
||||
|
||||
CNetClient* client = (CNetClient*)context;
|
||||
|
||||
CNetMessage* message = (CNetMessage*)event->GetParamRef();
|
||||
if (message)
|
||||
{
|
||||
if (message->GetType() == NMT_SIMULATION_COMMAND)
|
||||
{
|
||||
CSimulationMessage* simMessage = static_cast<CSimulationMessage*> (message);
|
||||
client->m_ClientTurnManager->OnSimulationMessage(simMessage);
|
||||
}
|
||||
else if (message->GetType() == NMT_SYNC_ERROR)
|
||||
{
|
||||
CSyncErrorMessage* syncMessage = static_cast<CSyncErrorMessage*> (message);
|
||||
client->m_ClientTurnManager->OnSyncError(syncMessage->m_Turn, syncMessage->m_HashExpected);
|
||||
}
|
||||
else if (message->GetType() == NMT_END_COMMAND_BATCH)
|
||||
{
|
||||
CEndCommandBatchMessage* endMessage = static_cast<CEndCommandBatchMessage*> (message);
|
||||
client->m_ClientTurnManager->FinishedAllCommands(endMessage->m_Turn);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: OnChat()
|
||||
// Desc:
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CNetClient::OnChat( void* pContext, CFsmEvent* pEvent )
|
||||
CStr CNetClient::GenerateGUID()
|
||||
{
|
||||
// Validate parameters
|
||||
if ( !pEvent || !pContext ) return false;
|
||||
// TODO: Ideally this will be guaranteed unique (and verified
|
||||
// cryptographically) since we'll rely on it to identify hosts
|
||||
// and associate them with player controls (e.g. to support
|
||||
// leaving/rejoining in-progress games), and we don't want
|
||||
// a host to masquerade as someone else.
|
||||
// For now, just try to pick a very random number.
|
||||
|
||||
CNetClient* pClient = ( CNetClient* )( ( FsmActionCtx* )pContext )->pHost;
|
||||
boost::random_device rng;
|
||||
boost::uniform_int<u32> dist(0, std::numeric_limits<u32>::max());
|
||||
boost::variate_generator<boost::random_device&, boost::uniform_int<u32> > gen(rng, dist);
|
||||
|
||||
if ( pEvent->GetType() == NMT_CHAT )
|
||||
CStr guid;
|
||||
for (size_t i = 0; i < 2; ++i)
|
||||
{
|
||||
CChatMessage* pMessage = ( CChatMessage* )pEvent->GetParamRef();
|
||||
if ( !pMessage ) return false;
|
||||
|
||||
g_Console->ReceivedChatMessage( pMessage->m_Sender, pMessage->m_Message );
|
||||
|
||||
if ( pClient->m_OnChat.Defined() )
|
||||
{
|
||||
CChatEvent evt( pMessage->m_Sender, pMessage->m_Message );
|
||||
pClient->m_OnChat.DispatchEvent( pClient->GetScript(), &evt );
|
||||
}
|
||||
char buf[32];
|
||||
sprintf_s(buf, ARRAY_SIZE(buf), "%08X", gen());
|
||||
guid += buf;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: OnStartGame()
|
||||
// Desc:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CNetClient::OnStartGame( void )
|
||||
{
|
||||
if ( m_OnStartGame.Defined() )
|
||||
{
|
||||
CStartGameEvent event;
|
||||
m_OnStartGame.DispatchEvent( GetScript(), &event );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: OnStartGame_()
|
||||
// Desc:
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CNetClient::OnStartGame_( void* pContext, CFsmEvent* pEvent )
|
||||
{
|
||||
// Validate parameters
|
||||
if ( !pEvent || !pContext ) return false;
|
||||
|
||||
CNetClient* pClient = ( CNetClient* )( ( FsmActionCtx* )pContext )->pHost;
|
||||
CNetSession* pSession = ( ( FsmActionCtx* )pContext )->pSession;
|
||||
|
||||
pClient->m_ClientTurnManager = new CNetClientTurnManager(*pClient->m_pGame->GetSimulation2(), *pClient, pClient->GetLocalPlayer()->GetPlayerID(), pSession->GetID());
|
||||
pClient->m_pGame->SetTurnManager(pClient->m_ClientTurnManager);
|
||||
|
||||
pClient->OnStartGame();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: OnPlayerJoin()
|
||||
// Desc:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CNetClient::OnPlayer( uint ID, const CStr& name )
|
||||
{
|
||||
CServerPlayer* pNewPlayer = new CServerPlayer( ID, name );
|
||||
if ( !pNewPlayer ) return;
|
||||
|
||||
// Store new player
|
||||
m_Players[ ID ] = pNewPlayer;
|
||||
|
||||
// Call JS Callback
|
||||
if ( m_OnPlayerJoin.Defined() )
|
||||
{
|
||||
CClientConnectEvent event( ID, name );
|
||||
m_OnPlayerJoin.DispatchEvent( GetScript(), &event );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: OnPlayerLeave()
|
||||
// Desc:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CNetClient::OnPlayerLeave( uint ID )
|
||||
{
|
||||
// Lookup player
|
||||
PlayerMap::iterator it = m_Players.find( ID );
|
||||
if ( it == m_Players.end() )
|
||||
{
|
||||
LOG( CLogger::Warning, LOG_CATEGORY, L"CNetClient::OnPlayerLeav(): No such player %d.", ID );
|
||||
return;
|
||||
}
|
||||
|
||||
// Call JS Callback
|
||||
if ( m_OnPlayerLeave.Defined() && it->second )
|
||||
{
|
||||
CClientDisconnectEvent event( it->second->GetSessionID(), it->second->GetNickname() );
|
||||
m_OnPlayerLeave.DispatchEvent( GetScript(), &event );
|
||||
}
|
||||
|
||||
// Remove player from internal map
|
||||
m_Players.erase( it );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: StartGame()
|
||||
// Desc:
|
||||
//-----------------------------------------------------------------------------
|
||||
int CNetClient::StartGame( void )
|
||||
{
|
||||
assert ( m_pGame );
|
||||
assert ( m_pGameAttributes );
|
||||
|
||||
if ( m_pGame->StartGame( m_pGameAttributes ) != PSRETURN_OK ) return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: GetLocalPlayer()
|
||||
// Desc:
|
||||
//-----------------------------------------------------------------------------
|
||||
CPlayer* CNetClient::GetLocalPlayer()
|
||||
{
|
||||
return m_pLocalPlayerSlot->GetPlayer();
|
||||
return guid;
|
||||
}
|
||||
|
@ -15,131 +15,195 @@
|
||||
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
*-----------------------------------------------------------------------------
|
||||
* FILE : NetClient.h
|
||||
* PROJECT : 0 A.D.
|
||||
* DESCRIPTION : Network client class interface file
|
||||
*-----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#ifndef NETCLIENT_H
|
||||
#define NETCLIENT_H
|
||||
|
||||
// INCLUDES
|
||||
#include "NetSession.h"
|
||||
#include "NetHost.h"
|
||||
#include "NetTurnManager.h"
|
||||
#include "network/fsm.h"
|
||||
#include "network/NetHost.h"
|
||||
#include "scriptinterface/ScriptVal.h"
|
||||
|
||||
#include "ps/CStr.h"
|
||||
#include "scripting/ScriptObject.h"
|
||||
#include "scripting/ScriptableObject.h"
|
||||
#include "ps/scripting/JSMap.h"
|
||||
|
||||
#include <map>
|
||||
#include <deque>
|
||||
|
||||
// DECLARATIONS
|
||||
class CGame;
|
||||
class CNetClientSession;
|
||||
class CNetClientTurnManager;
|
||||
class CNetServer;
|
||||
class ScriptInterface;
|
||||
|
||||
// NetClient session FSM states
|
||||
enum
|
||||
{
|
||||
NCS_CONNECT = 200,
|
||||
NCS_HANDSHAKE = 300,
|
||||
NCS_AUTHENTICATE = 400,
|
||||
NCS_PREGAME = 500,
|
||||
NCS_INGAME = 600
|
||||
NCS_UNCONNECTED,
|
||||
NCS_CONNECT,
|
||||
NCS_HANDSHAKE,
|
||||
NCS_AUTHENTICATE,
|
||||
NCS_INITIAL_GAMESETUP,
|
||||
NCS_PREGAME,
|
||||
NCS_LOADING,
|
||||
NCS_INGAME
|
||||
};
|
||||
|
||||
class CPlayerSlot;
|
||||
class CPlayer;
|
||||
class CGame;
|
||||
class CGameAttributes;
|
||||
class CServerPlayer;
|
||||
|
||||
typedef std::map< uint, CServerPlayer* > PlayerMap;
|
||||
|
||||
class CServerPlayer : public CJSObject< CServerPlayer >
|
||||
{
|
||||
NONCOPYABLE(CServerPlayer);
|
||||
|
||||
public:
|
||||
|
||||
CServerPlayer( uint sessionID, const CStr& nickname );
|
||||
~CServerPlayer( void );
|
||||
|
||||
static void ScriptingInit( void );
|
||||
uint GetSessionID( void ) const { return m_SessionID; }
|
||||
const CStr GetNickname( void ) const { return m_Nickname; }
|
||||
|
||||
private:
|
||||
|
||||
uint m_SessionID; // Player session ID
|
||||
CStr m_Nickname; // Player nickname
|
||||
};
|
||||
|
||||
class CNetClient: public CNetHost,
|
||||
public CJSObject<CNetClient>
|
||||
/**
|
||||
* Network client.
|
||||
* This code is run by every player (including the host, if they are not
|
||||
* a dedicated server).
|
||||
* It provides an interface between the GUI, the network (via CNetClientSession),
|
||||
* and the game (via CGame and CNetClientTurnManager).
|
||||
*/
|
||||
class CNetClient : public CFsm
|
||||
{
|
||||
NONCOPYABLE(CNetClient);
|
||||
|
||||
public:
|
||||
/**
|
||||
* Construct a client associated with the given game object.
|
||||
* The game must exist for the lifetime of this object.
|
||||
*/
|
||||
CNetClient(CGame* game);
|
||||
|
||||
CNetClient( ScriptInterface& scriptInterface, CGame* pGame, CGameAttributes* pGameAttributes );
|
||||
~CNetClient( void );
|
||||
virtual ~CNetClient();
|
||||
|
||||
bool CreateSession ( void );
|
||||
void OnPlayer ( uint ID, const CStr& name );
|
||||
void OnPlayerLeave ( uint ID );
|
||||
/**
|
||||
* Set the user's name that will be displayed to all players.
|
||||
* This must not be called after the connection setup.
|
||||
*/
|
||||
void SetUserName(const CStrW& username);
|
||||
|
||||
// Get a pointer to our player
|
||||
CPlayer* GetLocalPlayer();
|
||||
/**
|
||||
* Set up a connection to the remote networked server.
|
||||
* @param server IP address or host name to connect to
|
||||
* @return true on success, false on connection failure
|
||||
*/
|
||||
bool SetupConnection(const CStr& server);
|
||||
|
||||
CJSMap< PlayerMap > m_JsPlayers;
|
||||
/**
|
||||
* Set up a connection to the local server on the current machine.
|
||||
* @param server object to connect to
|
||||
*/
|
||||
void SetupLocalConnection(CNetServer& server);
|
||||
|
||||
CStr m_Nickname;
|
||||
CStr m_Password;
|
||||
/**
|
||||
* Poll the connection for messages from the server and process them, and send
|
||||
* any queued messages.
|
||||
* This must be called frequently (i.e. once per frame).
|
||||
*/
|
||||
void Poll();
|
||||
|
||||
CPlayerSlot *m_pLocalPlayerSlot;
|
||||
CGame *m_pGame;
|
||||
CGameAttributes *m_pGameAttributes;
|
||||
/**
|
||||
* Retrieves the next queued GUI message, and removes it from the queue.
|
||||
* The returned value is in the GetScriptInterface() JS context.
|
||||
*
|
||||
* This is the only mechanism for the networking code to send messages to
|
||||
* the GUI - it is pull-based (instead of push) so the engine code does not
|
||||
* need to know anything about the code structure of the GUI scripts.
|
||||
*
|
||||
* The structure of the messages is <code>{ "type": "...", ... }</code>.
|
||||
* The exact types and associated data are not specified anywhere - the
|
||||
* implementation and GUI scripts must make the same assumptions.
|
||||
*
|
||||
* @return next message, or the value 'undefined' if the queue is empty
|
||||
*/
|
||||
CScriptValRooted GuiPoll();
|
||||
|
||||
// JS event scripts
|
||||
CScriptObject m_OnStartGame;
|
||||
CScriptObject m_OnChat;
|
||||
CScriptObject m_OnConnectComplete;
|
||||
CScriptObject m_OnDisconnect;
|
||||
CScriptObject m_OnPlayerJoin;
|
||||
CScriptObject m_OnPlayerLeave;
|
||||
/**
|
||||
* Add a message to the queue, to be read by GuiPoll.
|
||||
* The script value must be in the GetScriptInterface() JS context.
|
||||
*/
|
||||
void PushGuiMessage(const CScriptValRooted& message);
|
||||
|
||||
static void ScriptingInit( void );
|
||||
int StartGame( void );
|
||||
/**
|
||||
* Return a concatenation of all messages in the GUI queue,
|
||||
* for test cases to easily verify the queue contents.
|
||||
*/
|
||||
std::wstring TestReadGuiMessages();
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Get the script interface associated with this network client,
|
||||
* which is equivalent to the one used by the CGame in the constructor.
|
||||
*/
|
||||
ScriptInterface& GetScriptInterface();
|
||||
|
||||
virtual bool SetupSession ( CNetSession* pSession );
|
||||
virtual bool HandleConnect ( CNetSession* pSession );
|
||||
virtual bool HandleDisconnect ( CNetSession *pSession );
|
||||
/**
|
||||
* Send a message to the server.
|
||||
* @param message message to send
|
||||
* @return true on success
|
||||
*/
|
||||
bool SendMessage(const CNetMessage* message);
|
||||
|
||||
virtual void OnConnectComplete ( void );
|
||||
virtual void OnStartGame ( void );
|
||||
/**
|
||||
* Call when the network connection has been successfully initiated.
|
||||
*/
|
||||
void HandleConnect();
|
||||
|
||||
/**
|
||||
* Call when the network connection has been lost.
|
||||
*/
|
||||
void HandleDisconnect();
|
||||
|
||||
/**
|
||||
* Call when a message has been received from the network.
|
||||
*/
|
||||
bool HandleMessage(CNetMessage* message);
|
||||
|
||||
/**
|
||||
* Call when the game has started and all data files have been loaded,
|
||||
* to signal to the server that we are ready to begin the game.
|
||||
*/
|
||||
void LoadFinished();
|
||||
|
||||
private:
|
||||
// Net message / FSM transition handlers
|
||||
static bool OnConnect(void* context, CFsmEvent* event);
|
||||
static bool OnHandshake(void* context, CFsmEvent* event);
|
||||
static bool OnHandshakeResponse(void* context, CFsmEvent* event);
|
||||
static bool OnAuthenticate(void* context, CFsmEvent* event);
|
||||
static bool OnChat(void* context, CFsmEvent* event);
|
||||
static bool OnGameSetup(void* context, CFsmEvent* event);
|
||||
static bool OnPlayerAssignment(void* context, CFsmEvent* event);
|
||||
static bool OnInGame(void* context, CFsmEvent* event);
|
||||
static bool OnGameStart(void* context, CFsmEvent* event);
|
||||
static bool OnLoadedGame(void* context, CFsmEvent* event);
|
||||
|
||||
static bool OnError ( void* pContext, CFsmEvent* pEvent );
|
||||
static bool OnPlayerJoin ( void* pContext, CFsmEvent* pEvent );
|
||||
static bool OnHandshake ( void* pContext, CFsmEvent* pEvent );
|
||||
static bool OnAuthenticate ( void* pContext, CFsmEvent* pEvent );
|
||||
static bool OnPreGame ( void* pContext, CFsmEvent* pEvent );
|
||||
static bool OnInGame ( void* pContext, CFsmEvent* pEvent );
|
||||
static bool OnChat ( void* pContext, CFsmEvent* pEvent );
|
||||
static bool OnStartGame_ ( void* pContext, CFsmEvent* pEvent );
|
||||
/**
|
||||
* Take ownership of a session object, and use it for all network communication.
|
||||
*/
|
||||
void SetAndOwnSession(CNetClientSession* session);
|
||||
|
||||
/**
|
||||
* Push a message onto the GUI queue listing the current player assignments.
|
||||
*/
|
||||
void PostPlayerAssignmentsToScript();
|
||||
|
||||
bool SetupConnection( JSContext *cx, uintN argc, jsval *argv );
|
||||
CGame *m_Game;
|
||||
CStrW m_UserName;
|
||||
|
||||
PlayerMap m_Players; // List of online players
|
||||
/// Current network session (or NULL if not connected)
|
||||
CNetClientSession* m_Session;
|
||||
|
||||
/// Turn manager associated with the current game (or NULL if we haven't started the game yet)
|
||||
CNetClientTurnManager* m_ClientTurnManager;
|
||||
|
||||
/// Unique-per-game identifier of this client, used to identify the sender of simulation commands
|
||||
u32 m_HostID;
|
||||
|
||||
/// Latest copy of game setup attributes heard from the server
|
||||
CScriptValRooted m_GameAttributes;
|
||||
|
||||
/// Latest copy of player assignments heard from the server
|
||||
PlayerAssignmentMap m_PlayerAssignments;
|
||||
|
||||
/// Globally unique identifier to distinguish users beyond the lifetime of a single network session
|
||||
CStr m_GUID;
|
||||
/// Initialise m_GUID with a random value
|
||||
CStr GenerateGUID();
|
||||
|
||||
/// Queue of messages for GuiPoll
|
||||
std::deque<CScriptValRooted> m_GuiMessageQueue;
|
||||
};
|
||||
|
||||
/// Global network client for the standard game
|
||||
extern CNetClient *g_NetClient;
|
||||
|
||||
#endif // NETCLIENT_H
|
||||
|
||||
|
@ -16,364 +16,40 @@
|
||||
*/
|
||||
|
||||
#include "precompiled.h"
|
||||
#include "NetHost.h"
|
||||
#include "NetSession.h"
|
||||
#include "NetMessage.h"
|
||||
|
||||
#include "NetHost.h"
|
||||
|
||||
#include "network/NetMessage.h"
|
||||
#include "ps/CLogger.h"
|
||||
#include "simulation2/Simulation2.h"
|
||||
|
||||
#include <enet/enet.h>
|
||||
|
||||
static const int ENET_DEFAULT_CHANNEL = 0;
|
||||
static const int CONNECT_TIMEOUT = 5000;
|
||||
static const int DISCONNECT_TIMEOUT = 1000;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: CNetHost()
|
||||
// Desc: Constructor
|
||||
//-----------------------------------------------------------------------------
|
||||
CNetHost::CNetHost(ScriptInterface& scriptInterface) :
|
||||
m_ScriptInterface(scriptInterface)
|
||||
bool CNetHost::SendMessage(const CNetMessage* message, ENetPeer* peer, const char* peerName)
|
||||
{
|
||||
m_Host = NULL;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: ~CNetHost()
|
||||
// Desc: Destructor
|
||||
//-----------------------------------------------------------------------------
|
||||
CNetHost::~CNetHost()
|
||||
{
|
||||
// Shutdown(); // TODO: should do something like this except don't call HandleDisconnect()
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: Create()
|
||||
// Desc: Creates a client host
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CNetHost::Create()
|
||||
{
|
||||
debug_assert(!m_Host);
|
||||
|
||||
// Create ENet host
|
||||
m_Host = enet_host_create(NULL, 1, 0, 0);
|
||||
if (!m_Host)
|
||||
ENetPacket* packet = CreatePacket(message);
|
||||
if (!packet)
|
||||
return false;
|
||||
|
||||
LOGMESSAGE(L"Net: Sending message %hs of size %lu to %s", message->ToString().c_str(), (unsigned long)packet->dataLength, peerName);
|
||||
|
||||
// Let ENet send the message to peer
|
||||
if (enet_peer_send(peer, DEFAULT_CHANNEL, packet) < 0)
|
||||
{
|
||||
LOGERROR(L"Net: Failed to send packet to peer");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't call enet_host_flush now - let it queue up all the packets
|
||||
// and send them during the next frame
|
||||
//
|
||||
// TODO: we should flush explicitly at some appropriate point before the next frame
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: Create()
|
||||
// Desc: Creates a server host
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CNetHost::Create(u16 port, size_t maxPeers)
|
||||
ENetPacket* CNetHost::CreatePacket(const CNetMessage* message)
|
||||
{
|
||||
ENetAddress addr;
|
||||
|
||||
// Bind to default host
|
||||
addr.host = ENET_HOST_ANY;
|
||||
addr.port = port;
|
||||
|
||||
// Create ENet server
|
||||
m_Host = enet_host_create(&addr, maxPeers, 0, 0);
|
||||
if (!m_Host)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: Shutdown()
|
||||
// Desc: Shuts down network server and releases any resources
|
||||
//-----------------------------------------------------------------------------
|
||||
void CNetHost::Shutdown()
|
||||
{
|
||||
// Disconnect and release each peer
|
||||
PeerSessionList::iterator it = m_PeerSessions.begin();
|
||||
for (; it != m_PeerSessions.end(); it++)
|
||||
{
|
||||
if (!it->pSession)
|
||||
continue;
|
||||
|
||||
Disconnect(it->pSession);
|
||||
|
||||
delete it->pSession;
|
||||
}
|
||||
|
||||
m_PeerSessions.clear();
|
||||
|
||||
// Destroy server
|
||||
if (m_Host)
|
||||
enet_host_destroy(m_Host);
|
||||
|
||||
m_Host = NULL;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: Connect()
|
||||
// Desc: Connects to the specified remote host
|
||||
// Note: Only clients use this method for connection to server
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CNetHost::Connect(const CStr& host, u16 port)
|
||||
{
|
||||
debug_assert(m_Host);
|
||||
|
||||
// Bind to specified host
|
||||
ENetAddress addr;
|
||||
addr.port = port;
|
||||
if (enet_address_set_host(&addr, host.c_str()) < 0)
|
||||
return false;
|
||||
|
||||
// Initiate connection, allocate one channel
|
||||
ENetPeer* pPeer = enet_host_connect(m_Host, &addr, 1);
|
||||
if (!pPeer)
|
||||
return false;
|
||||
|
||||
// Wait a few seconds for the connection to succeed
|
||||
// TODO: we ought to poll asynchronously so we can update the GUI while waiting
|
||||
ENetEvent event;
|
||||
if (enet_host_service(m_Host, &event, CONNECT_TIMEOUT) > 0 && event.type == ENET_EVENT_TYPE_CONNECT)
|
||||
{
|
||||
// Connection succeeded
|
||||
CNetSession* pNewSession = new CNetSession(this, event.peer);
|
||||
|
||||
if (!SetupSession(pNewSession))
|
||||
{
|
||||
delete pNewSession;
|
||||
return false;
|
||||
}
|
||||
|
||||
LOGMESSAGE(L"Net: Successfully connected to server %hs:%d", host.c_str(), port);
|
||||
|
||||
// Successfully handled?
|
||||
if (!HandleConnect(pNewSession))
|
||||
{
|
||||
delete pNewSession;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Store the only server session
|
||||
PeerSession item;
|
||||
item.pPeer = event.peer;
|
||||
item.pSession = pNewSession;
|
||||
m_PeerSessions.push_back(item);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
LOGERROR(L"Net: Connection to server %hs:%d failed", host.c_str(), port);
|
||||
|
||||
// Timed out or a host was disconnected
|
||||
enet_peer_reset(pPeer);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: ConnectAsync()
|
||||
// Desc: Connects to the specified remote host
|
||||
// Note: Only clients use this method for connection to server
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CNetHost::ConnectAsync(const CStr& host, u16 port)
|
||||
{
|
||||
debug_assert(m_Host);
|
||||
|
||||
// Bind to specified host
|
||||
ENetAddress addr;
|
||||
addr.port = port;
|
||||
if (enet_address_set_host(&addr, host.c_str()) < 0)
|
||||
return false;
|
||||
|
||||
// Initiate connection, allocate one channel
|
||||
ENetPeer* pPeer = enet_host_connect(m_Host, &addr, 1);
|
||||
if (!pPeer)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: Disconnect()
|
||||
// Desc: Disconnects the specified session from the host
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CNetHost::Disconnect(CNetSession* pSession)
|
||||
{
|
||||
// Validate parameters
|
||||
if (!pSession)
|
||||
return false;
|
||||
|
||||
debug_assert(m_Host);
|
||||
debug_assert(pSession->m_Peer);
|
||||
|
||||
// Disconnect peer
|
||||
enet_peer_disconnect(pSession->m_Peer, 0);
|
||||
|
||||
// Allow a few seconds for the disconnect to succeed
|
||||
ENetEvent event;
|
||||
while (enet_host_service(m_Host, &event, DISCONNECT_TIMEOUT) > 0)
|
||||
{
|
||||
switch (event.type)
|
||||
{
|
||||
case ENET_EVENT_TYPE_RECEIVE:
|
||||
|
||||
// Drop any received packets
|
||||
enet_packet_destroy(event.packet);
|
||||
break;
|
||||
|
||||
case ENET_EVENT_TYPE_DISCONNECT:
|
||||
|
||||
// Disconnect received for peer
|
||||
if (!HandleDisconnect(pSession))
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Disconnect attempt didn't succeed, force connection down
|
||||
enet_peer_reset(pSession->m_Peer);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: ProcessEvents()
|
||||
// Desc: Wait for events and shuttles packets between the host and its peers
|
||||
//-----------------------------------------------------------------------------
|
||||
void CNetHost::Poll()
|
||||
{
|
||||
debug_assert(m_Host);
|
||||
|
||||
// Poll host for events
|
||||
ENetEvent event;
|
||||
while (enet_host_service(m_Host, &event, 0) > 0)
|
||||
{
|
||||
switch (event.type)
|
||||
{
|
||||
case ENET_EVENT_TYPE_CONNECT:
|
||||
{
|
||||
// A new client has connected, handle it
|
||||
CNetSession* pSession = new CNetSession(this, event.peer);
|
||||
|
||||
// Setup new session
|
||||
if (!SetupSession(pSession))
|
||||
{
|
||||
delete pSession;
|
||||
break;
|
||||
}
|
||||
|
||||
LOGMESSAGE(L"Net: A new client connected from %x:%u", event.peer->address.host, event.peer->address.port);
|
||||
|
||||
// Successfully handled?
|
||||
if (!HandleConnect(pSession))
|
||||
{
|
||||
delete pSession;
|
||||
break;
|
||||
}
|
||||
|
||||
event.peer->data = pSession;
|
||||
|
||||
// Add new item to internal list
|
||||
PeerSession item;
|
||||
item.pPeer = event.peer;
|
||||
item.pSession = pSession;
|
||||
m_PeerSessions.push_back( item );
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ENET_EVENT_TYPE_DISCONNECT:
|
||||
{
|
||||
// Client has disconnected, handle it
|
||||
PeerSessionList::iterator it = m_PeerSessions.begin();
|
||||
for (; it != m_PeerSessions.end(); it++)
|
||||
{
|
||||
// Is this our session?
|
||||
if (it->pPeer == event.peer)
|
||||
{
|
||||
LOGMESSAGE(L"Net: %p disconnected", event.peer->data);
|
||||
|
||||
// Successfully handled?
|
||||
if (HandleDisconnect(it->pSession))
|
||||
m_PeerSessions.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ENET_EVENT_TYPE_RECEIVE:
|
||||
{
|
||||
// A new data packet was received from client, handle message
|
||||
PeerSessionList::iterator it = m_PeerSessions.begin();
|
||||
for (; it != m_PeerSessions.end(); it++)
|
||||
{
|
||||
// Is this our session?
|
||||
if (it->pPeer == event.peer)
|
||||
{
|
||||
bool ok = false;
|
||||
|
||||
// Create message from raw data
|
||||
CNetMessage* pNewMessage = CNetMessageFactory::CreateMessage(event.packet->data, event.packet->dataLength, m_ScriptInterface);
|
||||
if (pNewMessage)
|
||||
{
|
||||
LOGMESSAGE(L"Message %hs of size %lu was received from %p", pNewMessage->ToString().c_str(), (unsigned long)pNewMessage->GetSerializedLength(), event.peer->data);
|
||||
|
||||
ok = HandleMessageReceive(pNewMessage, it->pSession);
|
||||
|
||||
delete pNewMessage;
|
||||
}
|
||||
|
||||
// Done using the packet
|
||||
enet_packet_destroy(event.packet);
|
||||
|
||||
// TODO: what should we do if ok is false?
|
||||
// For now, just carry on as if nothing bad happened
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: Broadcast()
|
||||
// Desc: Broadcast the specified message to connected clients
|
||||
//-----------------------------------------------------------------------------
|
||||
void CNetHost::Broadcast(const CNetMessage* pMessage)
|
||||
{
|
||||
// Validate parameters
|
||||
if (!pMessage)
|
||||
return;
|
||||
|
||||
// Loop through the list of sessions and send the message to each
|
||||
for (uint i = 0; i < GetSessionCount(); i++)
|
||||
{
|
||||
CNetSession* pCurrSession = GetSession(i);
|
||||
if (!pCurrSession)
|
||||
continue;
|
||||
|
||||
SendMessage(pCurrSession, pMessage);
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: SendMessage()
|
||||
// Desc: Sends the specified message to peer
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CNetHost::SendMessage(const CNetSession* pSession, const CNetMessage* pMessage)
|
||||
{
|
||||
// Validate parameters
|
||||
if (!pMessage || !pSession)
|
||||
return false;
|
||||
|
||||
debug_assert(pSession->m_Peer);
|
||||
debug_assert(m_Host);
|
||||
|
||||
size_t size = pMessage->GetSerializedLength();
|
||||
size_t size = message->GetSerializedLength();
|
||||
|
||||
debug_assert(size); // else we'll fail when accessing the 0th element
|
||||
|
||||
@ -382,86 +58,12 @@ bool CNetHost::SendMessage(const CNetSession* pSession, const CNetMessage* pMess
|
||||
buffer.resize(size);
|
||||
|
||||
// Save message to internal buffer
|
||||
pMessage->Serialize(&buffer[0]);
|
||||
message->Serialize(&buffer[0]);
|
||||
|
||||
// Create a reliable packet
|
||||
ENetPacket* pPacket = enet_packet_create(&buffer[0], size, ENET_PACKET_FLAG_RELIABLE);
|
||||
if (!pPacket)
|
||||
return false;
|
||||
ENetPacket* packet = enet_packet_create(&buffer[0], size, ENET_PACKET_FLAG_RELIABLE);
|
||||
if (!packet)
|
||||
LOGERROR(L"Net: Failed to construct packet");
|
||||
|
||||
// Let ENet send the message to peer
|
||||
if (enet_peer_send(pSession->m_Peer, ENET_DEFAULT_CHANNEL, pPacket) < 0)
|
||||
{
|
||||
// ENet failed to send the packet
|
||||
LOGERROR(L"Net: Failed to send ENet packet to peer");
|
||||
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGMESSAGE(L"Net: Message %hs of size %lu was sent to %p",
|
||||
pMessage->ToString().c_str(), (unsigned long)size, pSession->m_Peer->data);
|
||||
}
|
||||
|
||||
// Don't call enet_host_flush - let it queue up all the packets
|
||||
// and send them during the next frame
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: ReceiveMessage()
|
||||
// Desc: Receives a message from client if incoming packets are available
|
||||
//-----------------------------------------------------------------------------
|
||||
CNetMessage* CNetHost::ReceiveMessage(const CNetSession* pSession)
|
||||
{
|
||||
// Validate parameters
|
||||
if (!pSession)
|
||||
return NULL;
|
||||
|
||||
debug_assert(pSession->m_Peer);
|
||||
|
||||
// Let ENet receive a message from peer
|
||||
ENetPacket* pPacket = enet_peer_receive(pSession->m_Peer, ENET_DEFAULT_CHANNEL);
|
||||
if (!pPacket)
|
||||
return NULL;
|
||||
|
||||
// Create new message
|
||||
return CNetMessageFactory::CreateMessage(pPacket->data, pPacket->dataLength, m_ScriptInterface);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: HandleMessageReceive()
|
||||
// Desc: Allow application to handle message recive
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CNetHost::HandleMessageReceive(CNetMessage* pMessage, CNetSession* pSession)
|
||||
{
|
||||
// Validate parameters
|
||||
if (!pSession || !pMessage)
|
||||
return false;
|
||||
|
||||
// Update FSM
|
||||
return pSession->Update(pMessage->GetType(), pMessage);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: GetSessionCount()
|
||||
// Desc: Returns the number of sessions the host manages
|
||||
//-----------------------------------------------------------------------------
|
||||
size_t CNetHost::GetSessionCount() const
|
||||
{
|
||||
return m_PeerSessions.size();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: GetSession()
|
||||
// Desc: Rteurns the session for the index
|
||||
//-----------------------------------------------------------------------------
|
||||
CNetSession* CNetHost::GetSession(size_t index)
|
||||
{
|
||||
// Validate parameter
|
||||
if (index >= GetSessionCount())
|
||||
return NULL;
|
||||
|
||||
return m_PeerSessions[index].pSession;
|
||||
return packet;
|
||||
}
|
||||
|
@ -18,130 +18,45 @@
|
||||
#ifndef NETHOST_H
|
||||
#define NETHOST_H
|
||||
|
||||
#include "fsm.h"
|
||||
|
||||
#include "ps/CStr.h"
|
||||
|
||||
#include <vector>
|
||||
/**
|
||||
* @file
|
||||
* Various declarations shared by networking code.
|
||||
*/
|
||||
|
||||
typedef struct _ENetPeer ENetPeer;
|
||||
typedef struct _ENetPacket ENetPacket;
|
||||
typedef struct _ENetHost ENetHost;
|
||||
class CNetSession;
|
||||
class CNetHost;
|
||||
class CNetMessage;
|
||||
class ScriptInterface;
|
||||
|
||||
struct PeerSession
|
||||
struct PlayerAssignment
|
||||
{
|
||||
ENetPeer* pPeer;
|
||||
CNetSession* pSession;
|
||||
CStrW m_Name; // player name
|
||||
i32 m_PlayerID; // the player that the given host controls, or -1 if none (observer)
|
||||
};
|
||||
|
||||
typedef std::vector<PeerSession> PeerSessionList;
|
||||
typedef std::map<CStr, PlayerAssignment> PlayerAssignmentMap; // map from GUID -> assignment
|
||||
|
||||
/**
|
||||
* Wrapper around ENet host concept
|
||||
*/
|
||||
class CNetHost
|
||||
{
|
||||
NONCOPYABLE(CNetHost);
|
||||
|
||||
public:
|
||||
|
||||
CNetHost(ScriptInterface& scriptInterface);
|
||||
virtual ~CNetHost();
|
||||
|
||||
bool Create();
|
||||
bool Create(u16 port, size_t maxPeers);
|
||||
void Shutdown();
|
||||
static const int DEFAULT_CHANNEL = 0;
|
||||
|
||||
/**
|
||||
* Indicates whether the host is currently a server
|
||||
*
|
||||
* @return Boolean indicating whether the host is a server
|
||||
*/
|
||||
virtual bool IsServer() const { return false; }
|
||||
|
||||
/**
|
||||
* Returns the number of sessions for the host
|
||||
*
|
||||
* @return The number of sessions
|
||||
*/
|
||||
size_t GetSessionCount() const;
|
||||
|
||||
/**
|
||||
* Returns the session object for the specified index
|
||||
*
|
||||
* @param index Index for session
|
||||
* @return Session object for index or NULL if not found
|
||||
*/
|
||||
CNetSession* GetSession(size_t index);
|
||||
|
||||
/**
|
||||
* Connects to foreign host synchronously
|
||||
*
|
||||
* @param host Foreign host name
|
||||
* @param port Port on which the foreign host listens
|
||||
* Transmit a message to the given peer.
|
||||
* @param message message to send
|
||||
* @param peer peer to send to
|
||||
* @param peerName name of peer for debug logs
|
||||
* @return true on success, false on failure
|
||||
*/
|
||||
bool Connect(const CStr& host, u16 port);
|
||||
static bool SendMessage(const CNetMessage* message, ENetPeer* peer, const char* peerName);
|
||||
|
||||
/**
|
||||
* Connects to foreign host asynchronously (i.e. without waiting for the connection
|
||||
* to succeed or to time out)
|
||||
*
|
||||
* @param host Foreign host name
|
||||
* @param port Port on which the foreign host listens
|
||||
* @return true on success, false on failure
|
||||
* Construct an ENet packet by serialising the given message.
|
||||
* @return NULL on failure
|
||||
*/
|
||||
bool ConnectAsync(const CStr& host, u16 port);
|
||||
|
||||
/**
|
||||
* Disconnects session from host
|
||||
*
|
||||
* @param pSession Session representing peer
|
||||
* @return true on success, false otherwise
|
||||
*/
|
||||
bool Disconnect(CNetSession* pSession);
|
||||
|
||||
/**
|
||||
* Listens for incoming connections and dispatches host events
|
||||
*/
|
||||
void Poll();
|
||||
|
||||
/**
|
||||
* Broadcast the specified message to connected clients
|
||||
*
|
||||
* @param pMessage Message to broadcast
|
||||
*/
|
||||
void Broadcast(const CNetMessage* pMessage);
|
||||
|
||||
/**
|
||||
* Send the specified message to client
|
||||
*
|
||||
* @param pMessage The message to send
|
||||
*/
|
||||
bool SendMessage(const CNetSession* pSession, const CNetMessage* pMessage);
|
||||
|
||||
protected:
|
||||
|
||||
// Allow application to handle new client connect
|
||||
virtual bool SetupSession(CNetSession* pSession) = 0;
|
||||
virtual bool HandleConnect(CNetSession* pSession) = 0;
|
||||
virtual bool HandleDisconnect(CNetSession* pSession) = 0;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Receive a message from client if available
|
||||
*/
|
||||
CNetMessage* ReceiveMessage(const CNetSession* pSession);
|
||||
|
||||
bool HandleMessageReceive(CNetMessage* pMessage, CNetSession* pSession);
|
||||
|
||||
ScriptInterface& m_ScriptInterface;
|
||||
|
||||
ENetHost* m_Host; // Represents this host
|
||||
PeerSessionList m_PeerSessions; // Session list of connected peers
|
||||
static ENetPacket* CreatePacket(const CNetMessage* message);
|
||||
};
|
||||
|
||||
#endif // NETHOST_H
|
||||
|
@ -1,164 +0,0 @@
|
||||
/* Copyright (C) 2009 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 0 A.D. is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
*-----------------------------------------------------------------------------
|
||||
* FILE : NetJsEvents.h
|
||||
* PROJECT : 0 A.D.
|
||||
* DESCRIPTION : Definitions for JavaScript events used by the network
|
||||
* system
|
||||
*-----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#ifndef NETJSEVENTS_H
|
||||
#define NETJSEVENTS_H
|
||||
|
||||
// INCLUDES
|
||||
#include "NetSession.h"
|
||||
#include "scripting/DOMEvent.h"
|
||||
|
||||
// DEFINES
|
||||
enum NetJsEvents
|
||||
{
|
||||
JS_EVENT_CLIENT_CONNECT,
|
||||
JS_EVENT_CONNECT_COMPLETE,
|
||||
JS_EVENT_CLIENT_DISCONNECT,
|
||||
JS_EVENT_DISCONNECT,
|
||||
JS_EVENT_START_GAME,
|
||||
JS_EVENT_CHAT,
|
||||
JS_EVENT_LAST
|
||||
};
|
||||
|
||||
class CStartGameEvent: public CScriptEvent
|
||||
{
|
||||
public:
|
||||
CStartGameEvent():
|
||||
CScriptEvent(L"startGame", JS_EVENT_START_GAME, false)
|
||||
{}
|
||||
};
|
||||
|
||||
class CChatEvent: public CScriptEvent
|
||||
{
|
||||
CStr m_Sender;
|
||||
CStr m_Message;
|
||||
|
||||
public:
|
||||
CChatEvent(const CStr& sender, const CStr& message):
|
||||
CScriptEvent(L"chat", JS_EVENT_CHAT, false ),
|
||||
m_Sender(sender),
|
||||
m_Message(message)
|
||||
{
|
||||
AddLocalProperty(L"sender", &m_Sender, true);
|
||||
AddLocalProperty(L"message", &m_Message, true);
|
||||
}
|
||||
};
|
||||
|
||||
class CConnectCompleteEvent: public CScriptEvent
|
||||
{
|
||||
CStr m_Message;
|
||||
bool m_Success;
|
||||
|
||||
public:
|
||||
CConnectCompleteEvent(const CStr& message, bool success):
|
||||
CScriptEvent(L"connectComplete", JS_EVENT_CONNECT_COMPLETE, false),
|
||||
m_Message(message),
|
||||
m_Success(success)
|
||||
{
|
||||
AddLocalProperty(L"message", &m_Message, true);
|
||||
AddLocalProperty(L"success", &m_Success, true);
|
||||
}
|
||||
};
|
||||
|
||||
class CDisconnectEvent: public CScriptEvent
|
||||
{
|
||||
CStrW m_Message;
|
||||
|
||||
public:
|
||||
CDisconnectEvent(const CStr& message):
|
||||
CScriptEvent(L"disconnect", JS_EVENT_DISCONNECT, false),
|
||||
m_Message(message)
|
||||
{
|
||||
AddLocalProperty(L"message", &m_Message, true);
|
||||
}
|
||||
};
|
||||
|
||||
class CClientConnectDisconnectCommon: public CScriptEvent
|
||||
{
|
||||
uint m_SessionID;
|
||||
CStr m_Name;
|
||||
CNetSession *m_Session;
|
||||
|
||||
public:
|
||||
|
||||
CClientConnectDisconnectCommon(const wchar_t* UNUSED(eventName), int UNUSED(eventType),
|
||||
int sessionID, const CStr& name, CNetSession* pSession)
|
||||
: CScriptEvent(L"clientConnect", JS_EVENT_CLIENT_CONNECT, false),
|
||||
m_SessionID(sessionID),
|
||||
m_Name(name),
|
||||
m_Session(pSession)
|
||||
{
|
||||
AddLocalProperty(L"id", &m_SessionID, true);
|
||||
AddLocalProperty(L"name", &m_Name, true);
|
||||
if (m_Session)
|
||||
AddLocalProperty(L"session", &m_Session, true);
|
||||
}
|
||||
};
|
||||
|
||||
struct CClientConnectEvent: public CClientConnectDisconnectCommon
|
||||
{
|
||||
CClientConnectEvent(int sessionID, const CStr& name):
|
||||
CClientConnectDisconnectCommon(
|
||||
L"clientConnect",
|
||||
JS_EVENT_CLIENT_CONNECT,
|
||||
sessionID,
|
||||
name,
|
||||
NULL)
|
||||
{}
|
||||
|
||||
CClientConnectEvent(CNetSession *pSession):
|
||||
CClientConnectDisconnectCommon(
|
||||
L"clientConnect",
|
||||
JS_EVENT_CLIENT_CONNECT,
|
||||
pSession->GetID(),
|
||||
pSession->GetName(),
|
||||
pSession)
|
||||
{}
|
||||
};
|
||||
|
||||
struct CClientDisconnectEvent: public CClientConnectDisconnectCommon
|
||||
{
|
||||
CClientDisconnectEvent(int sessionID, const CStr& name):
|
||||
CClientConnectDisconnectCommon(
|
||||
L"clientDisconnect",
|
||||
JS_EVENT_CLIENT_DISCONNECT,
|
||||
sessionID,
|
||||
name,
|
||||
NULL)
|
||||
{}
|
||||
|
||||
CClientDisconnectEvent(CNetSession *pSession):
|
||||
CClientConnectDisconnectCommon(
|
||||
L"clientDisconnect",
|
||||
JS_EVENT_CLIENT_DISCONNECT,
|
||||
pSession->GetID(),
|
||||
pSession->GetName(),
|
||||
pSession)
|
||||
{}
|
||||
};
|
||||
|
||||
#endif // NETJSEVENTS_H
|
||||
|
@ -15,20 +15,11 @@
|
||||
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
*-----------------------------------------------------------------------------
|
||||
* FILE : NetMessage.cpp
|
||||
* PROJECT : 0 A.D.
|
||||
* DESCRIPTION :
|
||||
*-----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
// INCLUDES
|
||||
#include "precompiled.h"
|
||||
#include "ps/CLogger.h"
|
||||
#include "Network.h"
|
||||
#include "NetMessage.h"
|
||||
|
||||
#include "ps/CLogger.h"
|
||||
|
||||
#include "ps/Game.h"
|
||||
#include "simulation2/Simulation2.h"
|
||||
|
||||
@ -36,9 +27,6 @@
|
||||
#define ALLNETMSGS_IMPLEMENT
|
||||
#include "NetMessages.h"
|
||||
|
||||
// DEFINES
|
||||
#define LOG_CATEGORY L"net"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: CNetMessage()
|
||||
// Desc: Constructor
|
||||
@ -46,7 +34,6 @@
|
||||
CNetMessage::CNetMessage( void )
|
||||
{
|
||||
m_Type = NMT_INVALID;
|
||||
m_Dirty = false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -56,7 +43,6 @@ CNetMessage::CNetMessage( void )
|
||||
CNetMessage::CNetMessage( NetMessageType type )
|
||||
{
|
||||
m_Type = type;
|
||||
m_Dirty = false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -65,7 +51,6 @@ CNetMessage::CNetMessage( NetMessageType type )
|
||||
//-----------------------------------------------------------------------------
|
||||
CNetMessage::~CNetMessage( void )
|
||||
{
|
||||
m_Dirty = false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -159,19 +144,15 @@ CNetMessage* CNetMessageFactory::CreateMessage(const void* pData,
|
||||
switch ( header.GetType() )
|
||||
{
|
||||
case NMT_GAME_SETUP:
|
||||
pNewMessage = new CGameSetupMessage;
|
||||
pNewMessage = new CGameSetupMessage(scriptInterface);
|
||||
break;
|
||||
|
||||
case NMT_ASSIGN_PLAYER_SLOT:
|
||||
pNewMessage = new CAssignPlayerSlotMessage;
|
||||
case NMT_PLAYER_ASSIGNMENT:
|
||||
pNewMessage = new CPlayerAssignmentMessage;
|
||||
break;
|
||||
|
||||
case NMT_PLAYER_CONFIG:
|
||||
pNewMessage = new CPlayerConfigMessage;
|
||||
break;
|
||||
|
||||
case NMT_PLAYER_JOIN:
|
||||
pNewMessage = new CPlayerJoinMessage;
|
||||
case NMT_LOADED_GAME:
|
||||
pNewMessage = new CLoadedGameMessage;
|
||||
break;
|
||||
|
||||
case NMT_SERVER_HANDSHAKE:
|
||||
@ -182,14 +163,6 @@ CNetMessage* CNetMessageFactory::CreateMessage(const void* pData,
|
||||
pNewMessage = new CSrvHandshakeResponseMessage;
|
||||
break;
|
||||
|
||||
case NMT_CONNECT_COMPLETE:
|
||||
pNewMessage = new CConnectCompleteMessage;
|
||||
break;
|
||||
|
||||
case NMT_ERROR:
|
||||
pNewMessage = new CErrorMessage;
|
||||
break;
|
||||
|
||||
case NMT_CLIENT_HANDSHAKE:
|
||||
pNewMessage = new CCliHandshakeMessage;
|
||||
break;
|
||||
@ -227,7 +200,7 @@ CNetMessage* CNetMessageFactory::CreateMessage(const void* pData,
|
||||
break;
|
||||
|
||||
default:
|
||||
LOG(CLogger::Error, LOG_CATEGORY, L"CNetMessageFactory::CreateMessage(): Unknown message received" );
|
||||
LOGERROR(L"CNetMessageFactory::CreateMessage(): Unknown message type '%d' received", header.GetType());
|
||||
break;
|
||||
}
|
||||
|
||||
@ -236,3 +209,18 @@ CNetMessage* CNetMessageFactory::CreateMessage(const void* pData,
|
||||
|
||||
return pNewMessage;
|
||||
}
|
||||
|
||||
CNetMessage* CNetMessageFactory::CloneMessage( const CNetMessage* message, ScriptInterface& scriptInterface )
|
||||
{
|
||||
// TODO: maybe this could be implemented more efficiently,
|
||||
// particularly for script messages where serialisation is
|
||||
// relatively expensive
|
||||
|
||||
size_t len = message->GetSerializedLength();
|
||||
u8* buffer = new u8[len];
|
||||
u8* newbuf = message->Serialize(buffer);
|
||||
if (!newbuf)
|
||||
return NULL;
|
||||
debug_assert(newbuf == buffer+len);
|
||||
return CreateMessage(buffer, len, scriptInterface);
|
||||
}
|
||||
|
@ -15,22 +15,10 @@
|
||||
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
*-----------------------------------------------------------------------------
|
||||
* FILE : NetMessage.h
|
||||
* PROJECT : 0 A.D.
|
||||
* DESCRIPTION : Defines the basic interface for network messages
|
||||
*-----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#ifndef NETMESSAGE_H
|
||||
#define NETMESSAGE_H
|
||||
|
||||
// INCLUDES
|
||||
#include "Serialization.h"
|
||||
#include "ps/Vector2D.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
// We need the enum from NetMessages.h, but we can't create any classes in
|
||||
// NetMessages.h, since they in turn require CNetMessage to be defined
|
||||
@ -38,13 +26,9 @@
|
||||
#include "NetMessages.h"
|
||||
#undef ALLNETMSGS_DONT_CREATE_NMTS
|
||||
|
||||
/*
|
||||
CLASS : CNetMessage
|
||||
DESCRIPTION : CNetMessage is the base class for all network messages
|
||||
exchanged within the game.
|
||||
NOTES :
|
||||
/**
|
||||
* The base class for all network messages exchanged within the game.
|
||||
*/
|
||||
|
||||
class CNetMessage : public ISerializable
|
||||
{
|
||||
NONCOPYABLE(CNetMessage);
|
||||
@ -58,27 +42,11 @@ public:
|
||||
virtual ~CNetMessage( void );
|
||||
|
||||
/**
|
||||
* Retrieves the message header. If changes are made on header,SetDirty
|
||||
* must be called on the header's message
|
||||
*
|
||||
* @return Message header
|
||||
* Retrieves the message type.
|
||||
* @return Message type
|
||||
*/
|
||||
//const CNetMessageHeader& GetHeader( void ) const { return m_Header; }
|
||||
NetMessageType GetType( void ) const { return m_Type; }
|
||||
|
||||
/**
|
||||
* Returns whether the message has changed since its last use
|
||||
*
|
||||
* @return true if it changed or false otherwise
|
||||
*/
|
||||
bool GetDirty( void ) const { return m_Dirty; }
|
||||
|
||||
/**
|
||||
* Specify the message has changed since its last use
|
||||
*
|
||||
*/
|
||||
void SetDirty( void ) { m_Dirty = true; }
|
||||
|
||||
/**
|
||||
* Serialize the message into the specified buffer parameter. The size
|
||||
* required by the buffer parameter can be found by a call to
|
||||
@ -102,23 +70,6 @@ public:
|
||||
*/
|
||||
virtual const u8* Deserialize( const u8* pStart, const u8* pEnd );
|
||||
|
||||
/**
|
||||
* Deserializes the specified message from the specified buffer using
|
||||
* registered deserializers.
|
||||
*
|
||||
* @param messageType Message type
|
||||
* @param pBuffer Buffer from which to deserialize
|
||||
* @param bufferSize The size in bytes of the buffer
|
||||
* @return A pointer to a newly created
|
||||
* CNetMessage, or NULL if the message was
|
||||
* not correctly deserialized.
|
||||
*/
|
||||
static CNetMessage* Deserialize(
|
||||
NetMessageType type,
|
||||
const u8* pBuffer,
|
||||
uint bufferSize );
|
||||
//static CNetMessage* Deserialize(ENetMessageType type, u8 *buffer, uint length);
|
||||
|
||||
/**
|
||||
* Retrieves the size in bytes of the serialized message. Before calling
|
||||
* Serialize, the memory size for the buffer where to serialize the message
|
||||
@ -136,7 +87,6 @@ public:
|
||||
virtual CStr ToString( void ) const;
|
||||
|
||||
private:
|
||||
bool m_Dirty; // Message has been modified
|
||||
NetMessageType m_Type; // Message type
|
||||
};
|
||||
|
||||
@ -159,13 +109,13 @@ public:
|
||||
*/
|
||||
static CNetMessage* CreateMessage( const void* pData, size_t dataSize, ScriptInterface& scriptInterface );
|
||||
|
||||
private:
|
||||
|
||||
// Not implemented
|
||||
CNetMessageFactory( void );
|
||||
~CNetMessageFactory( void );
|
||||
CNetMessageFactory( const CNetMessageFactory& );
|
||||
CNetMessageFactory& operator=( const CNetMessageFactory& );
|
||||
/**
|
||||
* Clone a message object into a new scripting context.
|
||||
* @param message message to clone (can come from any script context)
|
||||
* @param scriptInterface script context to use for the new message
|
||||
* @return new message, or NULL on failure
|
||||
*/
|
||||
static CNetMessage* CloneMessage( const CNetMessage* message, ScriptInterface& scriptInterface );
|
||||
};
|
||||
|
||||
/**
|
||||
@ -190,6 +140,24 @@ private:
|
||||
ScriptInterface& m_ScriptInterface;
|
||||
};
|
||||
|
||||
/**
|
||||
* Special message type for updated to game startup settings.
|
||||
*/
|
||||
class CGameSetupMessage : public CNetMessage
|
||||
{
|
||||
public:
|
||||
CGameSetupMessage(ScriptInterface& scriptInterface);
|
||||
CGameSetupMessage(ScriptInterface& scriptInterface, jsval data);
|
||||
virtual u8* Serialize(u8* pBuffer) const;
|
||||
virtual const u8* Deserialize(const u8* pStart, const u8* pEnd);
|
||||
virtual size_t GetSerializedLength() const;
|
||||
virtual CStr ToString() const;
|
||||
|
||||
CScriptValRooted m_Data;
|
||||
private:
|
||||
ScriptInterface& m_ScriptInterface;
|
||||
};
|
||||
|
||||
// This time, the classes are created
|
||||
#include "NetMessages.h"
|
||||
|
||||
|
@ -19,6 +19,7 @@
|
||||
|
||||
#include "NetMessage.h"
|
||||
|
||||
#include "lib/utf8.h"
|
||||
#include "scriptinterface/ScriptInterface.h"
|
||||
#include "simulation2/serialization/BinarySerializer.h"
|
||||
#include "simulation2/serialization/StdDeserializer.h"
|
||||
@ -145,12 +146,58 @@ size_t CSimulationMessage::GetSerializedLength() const
|
||||
|
||||
CStr CSimulationMessage::ToString() const
|
||||
{
|
||||
std::string source;
|
||||
|
||||
if (!m_ScriptInterface.CallFunction(m_Data.get(), "toSource", source))
|
||||
source = "ERROR";
|
||||
std::string source = utf8_from_wstring(m_ScriptInterface.ToString(m_Data.get()));
|
||||
|
||||
std::stringstream stream;
|
||||
stream << "CSimulationMessage { m_Client: " << m_Client << ", m_Player: " << m_Player << ", m_Turn: " << m_Turn << ", m_Data: " << source << " }";
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
|
||||
CGameSetupMessage::CGameSetupMessage(ScriptInterface& scriptInterface) :
|
||||
CNetMessage(NMT_GAME_SETUP), m_ScriptInterface(scriptInterface)
|
||||
{
|
||||
}
|
||||
|
||||
CGameSetupMessage::CGameSetupMessage(ScriptInterface& scriptInterface, jsval data) :
|
||||
CNetMessage(NMT_GAME_SETUP), m_ScriptInterface(scriptInterface),
|
||||
m_Data(scriptInterface.GetContext(), data)
|
||||
{
|
||||
}
|
||||
|
||||
u8* CGameSetupMessage::Serialize(u8* pBuffer) const
|
||||
{
|
||||
// TODO: ought to handle serialization exceptions
|
||||
|
||||
u8* pos = CNetMessage::Serialize(pBuffer);
|
||||
CBufferBinarySerializer serializer(m_ScriptInterface, pos);
|
||||
serializer.ScriptVal("command", m_Data);
|
||||
return serializer.GetBuffer();
|
||||
}
|
||||
|
||||
const u8* CGameSetupMessage::Deserialize(const u8* pStart, const u8* pEnd)
|
||||
{
|
||||
// TODO: ought to handle serialization exceptions
|
||||
|
||||
const u8* pos = CNetMessage::Deserialize(pStart, pEnd);
|
||||
std::istringstream stream(std::string(pos, pEnd));
|
||||
CStdDeserializer deserializer(m_ScriptInterface, stream);
|
||||
deserializer.ScriptVal(m_Data);
|
||||
return pEnd;
|
||||
}
|
||||
|
||||
size_t CGameSetupMessage::GetSerializedLength() const
|
||||
{
|
||||
CLengthBinarySerializer serializer(m_ScriptInterface);
|
||||
serializer.ScriptVal("command", m_Data);
|
||||
return CNetMessage::GetSerializedLength() + serializer.GetLength();
|
||||
}
|
||||
|
||||
CStr CGameSetupMessage::ToString() const
|
||||
{
|
||||
std::string source = utf8_from_wstring(m_ScriptInterface.ToString(m_Data.get()));
|
||||
|
||||
std::stringstream stream;
|
||||
stream << "CGameSetupMessage { m_Data: " << source << " }";
|
||||
return stream.str();
|
||||
}
|
||||
|
@ -16,36 +16,28 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
*-----------------------------------------------------------------------------
|
||||
* FILE : NetMessages.h
|
||||
* PROJECT : 0 A.D.
|
||||
* DESCRIPTION : The list of messages used by the network subsystem
|
||||
*-----------------------------------------------------------------------------
|
||||
* @file
|
||||
* The list of messages used by the network subsystem.
|
||||
*/
|
||||
|
||||
#ifndef NETMESSAGES_H
|
||||
#define NETMESSAGES_H
|
||||
|
||||
// INCLUDES
|
||||
#include "ps/CStr.h"
|
||||
#include "scripting/JSSerialization.h"
|
||||
#include "scriptinterface/ScriptVal.h"
|
||||
|
||||
// DEFINES
|
||||
#define PS_PROTOCOL_MAGIC 0x5073013f // 'P', 's', 0x01, '?'
|
||||
#define PS_PROTOCOL_MAGIC_RESPONSE 0x50630121 // 'P', 'c', 0x01, '!'
|
||||
#define PS_PROTOCOL_VERSION 0x01010002 // Arbitrary protocol
|
||||
#define PS_PROTOCOL_VERSION 0x01010003 // Arbitrary protocol
|
||||
#define PS_DEFAULT_PORT 0x5073 // 'P', 's'
|
||||
|
||||
// Defines the list of message types. The order of the list must not change
|
||||
// Defines the list of message types. The order of the list must not change.
|
||||
// The message types having a negative value are used internally and not sent
|
||||
// over the network. The message types used for network communication have
|
||||
// positive values.
|
||||
enum NetMessageType
|
||||
{
|
||||
NMT_ERROR = -256, // Delivery of error states
|
||||
NMT_CONNECT_COMPLETE, // Connection is complete
|
||||
NMT_CLOSE_REQUEST, // Close connection request
|
||||
NMT_CONNECT_COMPLETE = -256, // Connection is complete
|
||||
NMT_INVALID = 0, // Invalid message
|
||||
NMT_SERVER_HANDSHAKE, // Handshake stage
|
||||
NMT_CLIENT_HANDSHAKE,
|
||||
@ -53,18 +45,11 @@ enum NetMessageType
|
||||
NMT_AUTHENTICATE, // Authentication stage
|
||||
NMT_AUTHENTICATE_RESULT,
|
||||
NMT_CHAT, // Common chat message
|
||||
NMT_PLAYER_JOIN, // Pre-game stage
|
||||
NMT_PLAYER_LEAVE,
|
||||
NMT_GAME_SETUP,
|
||||
NMT_ASSIGN_PLAYER_SLOT,
|
||||
NMT_PLAYER_CONFIG,
|
||||
NMT_FILES_REQUIRED,
|
||||
NMT_FILE_REQUEST,
|
||||
NMT_FILE_CHUNK,
|
||||
NMT_FILE_CHUNK_ACK,
|
||||
NMT_FILE_PROGRESS,
|
||||
NMT_PLAYER_ASSIGNMENT,
|
||||
NMT_LOADED_GAME,
|
||||
NMT_GAME_START,
|
||||
NMT_END_COMMAND_BATCH, // In-game stage
|
||||
NMT_END_COMMAND_BATCH,
|
||||
NMT_SYNC_CHECK,
|
||||
NMT_SYNC_ERROR,
|
||||
NMT_SIMULATION_COMMAND,
|
||||
@ -80,22 +65,6 @@ enum AuthenticateResultCode
|
||||
ARC_NICK_INVALID,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
CHAT_RECIPIENT_FIRST = 0xFFFD,
|
||||
CHAT_RECIPIENT_ENEMIES = 0xFFFD,
|
||||
CHAT_RECIPIENT_ALLIES = 0xFFFE,
|
||||
CHAT_RECIPIENT_ALL = 0xFFFF
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
ASSIGN_OPEN,
|
||||
ASSIGN_CLOSED,
|
||||
ASSIGN_AI,
|
||||
ASSIGN_SESSION
|
||||
};
|
||||
|
||||
#endif // NETMESSAGES_H
|
||||
|
||||
#ifdef CREATING_NMT
|
||||
@ -125,14 +94,14 @@ START_NMT_CLASS_(SrvHandshakeResponse, NMT_SERVER_HANDSHAKE_RESPONSE)
|
||||
END_NMT_CLASS()
|
||||
|
||||
START_NMT_CLASS_(Authenticate, NMT_AUTHENTICATE)
|
||||
NMT_FIELD(CStr8, m_GUID)
|
||||
NMT_FIELD(CStrW, m_Name)
|
||||
//NMT_FIELD(CPasswordHash, m_Password)
|
||||
NMT_FIELD(CStrW, m_Password)
|
||||
END_NMT_CLASS()
|
||||
|
||||
START_NMT_CLASS_(AuthenticateResult, NMT_AUTHENTICATE_RESULT)
|
||||
NMT_FIELD_INT(m_Code, u32, 4)
|
||||
NMT_FIELD_INT(m_SessionID, u32, 2)
|
||||
NMT_FIELD_INT(m_HostID, u32, 2)
|
||||
NMT_FIELD(CStrW, m_Message)
|
||||
END_NMT_CLASS()
|
||||
|
||||
@ -142,36 +111,15 @@ START_NMT_CLASS_(Chat, NMT_CHAT)
|
||||
NMT_FIELD(CStrW, m_Message)
|
||||
END_NMT_CLASS()
|
||||
|
||||
START_NMT_CLASS_(PlayerJoin, NMT_PLAYER_JOIN)
|
||||
NMT_START_ARRAY(m_Clients)
|
||||
NMT_FIELD_INT(m_SessionID, u32, 2)
|
||||
NMT_FIELD(CStr, m_Name)
|
||||
NMT_END_ARRAY()
|
||||
END_NMT_CLASS()
|
||||
|
||||
START_NMT_CLASS_(PlayerLeave, NMT_PLAYER_LEAVE)
|
||||
NMT_FIELD_INT(m_SessionID, u32, 2)
|
||||
END_NMT_CLASS()
|
||||
|
||||
START_NMT_CLASS_(GameSetup, NMT_GAME_SETUP)
|
||||
NMT_START_ARRAY(m_Values)
|
||||
START_NMT_CLASS_(PlayerAssignment, NMT_PLAYER_ASSIGNMENT)
|
||||
NMT_START_ARRAY(m_Hosts)
|
||||
NMT_FIELD(CStr8, m_GUID)
|
||||
NMT_FIELD(CStrW, m_Name)
|
||||
NMT_FIELD(CStrW, m_Value)
|
||||
NMT_FIELD_INT(m_PlayerID, u8, 1)
|
||||
NMT_END_ARRAY()
|
||||
END_NMT_CLASS()
|
||||
|
||||
START_NMT_CLASS_(AssignPlayerSlot, NMT_ASSIGN_PLAYER_SLOT)
|
||||
NMT_FIELD_INT(m_SlotID, u32, 2)
|
||||
NMT_FIELD_INT(m_Assignment, u32, 1)
|
||||
NMT_FIELD_INT(m_SessionID, u32, 2) // Only applicable for PS_ASSIGN_SESSION
|
||||
END_NMT_CLASS()
|
||||
|
||||
START_NMT_CLASS_(PlayerConfig, NMT_PLAYER_CONFIG)
|
||||
NMT_FIELD_INT(m_PlayerID, u32, 2)
|
||||
NMT_START_ARRAY(m_Values)
|
||||
NMT_FIELD(CStrW, m_Name)
|
||||
NMT_FIELD(CStrW, m_Value)
|
||||
NMT_END_ARRAY()
|
||||
START_NMT_CLASS_(LoadedGame, NMT_LOADED_GAME)
|
||||
END_NMT_CLASS()
|
||||
|
||||
START_NMT_CLASS_(GameStart, NMT_GAME_START)
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -15,54 +15,36 @@
|
||||
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
*-----------------------------------------------------------------------------
|
||||
* FILE : NetServer.h
|
||||
* PROJECT : 0 A.D.
|
||||
* DESCRIPTION : Network server class interface file
|
||||
*-----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#ifndef NETSERVER_H
|
||||
#define NETSERVER_H
|
||||
|
||||
// INCLUDES
|
||||
#include "Network.h"
|
||||
#include "NetHost.h"
|
||||
#include "NetSession.h"
|
||||
#include "NetTurnManager.h"
|
||||
#include "scripting/ScriptableObject.h"
|
||||
#include "ps/scripting/JSMap.h"
|
||||
#include "ps/Player.h"
|
||||
#include "ps/Game.h"
|
||||
#include "scripting/ScriptObject.h"
|
||||
|
||||
#include <map>
|
||||
#include "scriptinterface/ScriptVal.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
// DECLARATIONS
|
||||
#define SERVER_SESSIONID 1
|
||||
#define CLIENT_MIN_SESSIONID 100
|
||||
#define MAX_CLIENTS 8
|
||||
#define MAX_OBSERVERS 5
|
||||
#define DEFAULT_SERVER_NAME L"Noname Server"
|
||||
#define DEFAULT_PLAYER_NAME L"Noname Player"
|
||||
#define DEFAULT_WELCOME_MESSAGE L"Noname Server Welcome Message"
|
||||
#define DEFAULT_HOST_PORT 0x5073
|
||||
|
||||
class CGameAttributes;
|
||||
class CNetClientSessionLocal;
|
||||
class CNetServerSession;
|
||||
class CNetServerTurnManager;
|
||||
class CFsmEvent;
|
||||
class ScriptInterface;
|
||||
class CPlayerAssignmentMessage;
|
||||
|
||||
enum NetServerState
|
||||
{
|
||||
// We haven't opened the port yet, we're just setting some stuff up.
|
||||
// This is probably equivalent to the first "Start Network Game" screen
|
||||
SERVER_STATE_PREBIND,
|
||||
SERVER_STATE_UNCONNECTED,
|
||||
|
||||
// The server is open and accepting connections. This is the screen where
|
||||
// rules are set up by the operator and where players join and select civs
|
||||
// and stuff.
|
||||
SERVER_STATE_PREGAME,
|
||||
|
||||
// All the hosts are connected and are loading the game
|
||||
SERVER_STATE_LOADING,
|
||||
|
||||
// The one with all the killing ;-)
|
||||
SERVER_STATE_INGAME,
|
||||
|
||||
@ -71,214 +53,157 @@ enum NetServerState
|
||||
SERVER_STATE_POSTGAME
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
NSS_HANDSHAKE = 1300,
|
||||
NSS_AUTHENTICATE = 1400,
|
||||
NSS_PREGAME = 1500,
|
||||
NSS_INGAME = 1600
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
NMT_APP_PLAYER_LEAVE = NMT_LAST + 100,
|
||||
NMT_APP_PREGAME = NMT_LAST + 200,
|
||||
NMT_APP_OBSERVER = NMT_LAST + 300
|
||||
};
|
||||
|
||||
typedef std::map< uint, CNetSession* > IDSessionMap;
|
||||
typedef std::vector< CNetSession* > SessionList;
|
||||
|
||||
/*
|
||||
CLASS : CNetServer
|
||||
DESCRIPTION : CNetServer implements a network server for the game.
|
||||
It receives data and connection requests from clients.
|
||||
Under the hood, it uses ENet library to manage connected
|
||||
peers and bandwidth among these.
|
||||
NOTES :
|
||||
/**
|
||||
* Server session representation of client state
|
||||
*/
|
||||
enum
|
||||
{
|
||||
NSS_HANDSHAKE,
|
||||
NSS_AUTHENTICATE,
|
||||
NSS_PREGAME,
|
||||
NSS_INGAME
|
||||
};
|
||||
|
||||
class CNetServer : public CNetHost,
|
||||
public CJSObject<CNetServer>
|
||||
/**
|
||||
* Network server.
|
||||
* Handles all the coordination between players.
|
||||
* One person runs this object, and every player (including the host) connects their CNetClient to it.
|
||||
*
|
||||
* TODO: ideally the ENet server would run in a separate thread so it can receive
|
||||
* and forward messages with minimal latency. But that's not supported now.
|
||||
*
|
||||
* TODO: we need to be much more careful at handling client states, to cope with
|
||||
* e.g. people being in the middle of connecting or authenticating when we start the game.
|
||||
*/
|
||||
class CNetServer
|
||||
{
|
||||
NONCOPYABLE(CNetServer);
|
||||
public:
|
||||
|
||||
CNetServer( ScriptInterface& scriptInterface, CGame* pGame, CGameAttributes* pGameAttributes );
|
||||
virtual ~CNetServer( void );
|
||||
|
||||
bool Start ( JSContext *pContext, uintN argc, jsval *argv );
|
||||
CNetServer();
|
||||
virtual ~CNetServer();
|
||||
|
||||
/**
|
||||
* Returns true indicating the host acts as a server
|
||||
*
|
||||
* @return Always true
|
||||
* Get the current server's connection/game state.
|
||||
*/
|
||||
virtual bool IsServer( void ) const { return true; }
|
||||
NetServerState GetState() const { return m_State; }
|
||||
|
||||
/**
|
||||
* Adds a new session to the list of sessions
|
||||
*
|
||||
* @param pSession New session to add
|
||||
* Begin listening for network connections.
|
||||
* @return true on success, false on error (e.g. port already in use)
|
||||
*/
|
||||
void AddSession( CNetSession* pSession );
|
||||
bool SetupConnection();
|
||||
|
||||
/**
|
||||
* Removes the specified session from the list of sessions. If the session
|
||||
* isn't found it returns NULL otherwise it returns the session object found.
|
||||
*
|
||||
* @param pSession Session to remove
|
||||
* @return The session object if found, NULL otherwise
|
||||
* Poll the connections for messages from clients and process them, and send
|
||||
* any queued messages.
|
||||
* This must be called frequently (i.e. once per frame).
|
||||
*/
|
||||
CNetSession* RemoveSession( CNetSession* pSession );
|
||||
virtual void Poll();
|
||||
|
||||
/**
|
||||
* Returns the session object for the specified ID
|
||||
*
|
||||
* @param sessionID The session ID
|
||||
* @return A pointer to session for the specified ID or
|
||||
* NULL if not found
|
||||
* Send a message to the given network peer.
|
||||
*/
|
||||
CNetSession* GetSessionByID( uint sessionID );
|
||||
bool SendMessage(ENetPeer* peer, const CNetMessage* message);
|
||||
|
||||
/**
|
||||
* Send a message to the given local client.
|
||||
*/
|
||||
void SendLocalMessage(CNetClientSessionLocal& clientSession, const CNetMessage* message);
|
||||
|
||||
/**
|
||||
* Send a message to all clients who have completed the full connection process
|
||||
* (i.e. are in the pre-game or in-game states).
|
||||
*/
|
||||
bool Broadcast(const CNetMessage* message);
|
||||
|
||||
/**
|
||||
* Register a local client with this server (equivalent to a remote client connecting
|
||||
* over the network).
|
||||
*/
|
||||
void AddLocalClientSession(CNetClientSessionLocal& clientSession);
|
||||
|
||||
/**
|
||||
* Call from the GUI to update the player assignments.
|
||||
* The given GUID will be (re)assigned to the given player ID.
|
||||
* Any player currently using that ID will be unassigned.
|
||||
* The changes will be propagated to all clients.
|
||||
*/
|
||||
void AssignPlayer(int playerID, const CStr& guid);
|
||||
|
||||
/**
|
||||
* Call from the GUI to notify all clients that they should start loading the game.
|
||||
*/
|
||||
void StartGame();
|
||||
|
||||
/**
|
||||
* Call from the GUI to update the game setup attributes.
|
||||
* This must be called at least once before starting the game.
|
||||
* The changes will be propagated to all clients.
|
||||
* @param attrs game attributes, in the script context of GetScriptInterface()
|
||||
*/
|
||||
void UpdateGameAttributes(const CScriptValRooted& attrs);
|
||||
|
||||
/**
|
||||
* Get the script context used for game attributes.
|
||||
*/
|
||||
ScriptInterface& GetScriptInterface();
|
||||
|
||||
protected:
|
||||
|
||||
virtual bool SetupSession ( CNetSession* pSession );
|
||||
virtual bool HandleConnect ( CNetSession* pSession );
|
||||
virtual bool HandleDisconnect ( CNetSession *pSession );
|
||||
/// Callback for autostart; called when a player has finished connecting
|
||||
virtual void OnAddPlayer() { }
|
||||
/// Callback for autostart; called when a player has left the game
|
||||
virtual void OnRemovePlayer() { }
|
||||
|
||||
private:
|
||||
void AddPlayer(const CStr& guid, const CStrW& name);
|
||||
void RemovePlayer(const CStr& guid);
|
||||
void SendPlayerAssignments();
|
||||
PlayerAssignmentMap m_PlayerAssignments;
|
||||
|
||||
CScriptValRooted m_GameAttributes;
|
||||
|
||||
void SetupSession(CNetServerSession* session);
|
||||
bool HandleConnect(CNetServerSession* session);
|
||||
bool HandleDisconnect(CNetServerSession* session);
|
||||
void OnUserJoin(CNetServerSession* session);
|
||||
|
||||
static bool OnClientHandshake(void* context, CFsmEvent* event);
|
||||
static bool OnAuthenticate(void* context, CFsmEvent* event);
|
||||
static bool OnInGame(void* context, CFsmEvent* event);
|
||||
static bool OnChat(void* context, CFsmEvent* event);
|
||||
static bool OnLoadedGame(void* context, CFsmEvent* event);
|
||||
|
||||
void CheckGameLoadStatus(CNetServerSession* changedSession);
|
||||
|
||||
void ConstructPlayerAssignmentMessage(CPlayerAssignmentMessage& message);
|
||||
|
||||
bool HandleMessageReceive(const CNetMessage* message, CNetServerSession* session);
|
||||
|
||||
/**
|
||||
* Loads the player properties into the specified message
|
||||
*
|
||||
* @param pMessage Message where to load player properties
|
||||
* @param pPlayer Player for which we load the properties
|
||||
* Internal script context for (de)serializing script messages.
|
||||
* (TODO: we shouldn't bother deserializing (except for debug printing of messages),
|
||||
* we should just forward messages blindly and efficiently.)
|
||||
*/
|
||||
void BuildPlayerConfigMessage(
|
||||
CPlayerConfigMessage* pMessage,
|
||||
CPlayer* pPlayer );
|
||||
ScriptInterface* m_ScriptInterface;
|
||||
|
||||
/**
|
||||
* Callback function used by the BuildPlayerSetupMessage to iterate over
|
||||
* the player properties. It will be called for each property of the player
|
||||
*
|
||||
* @param name Property name
|
||||
* @param pProperty Pointer to player property
|
||||
* @param pData Context pointer passed on iteration startup
|
||||
*/
|
||||
static void PlayerConfigMessageCallback(
|
||||
const CStrW& name,
|
||||
ISynchedJSProperty* pProperty,
|
||||
void* pData );
|
||||
ENetHost* m_Host;
|
||||
std::vector<ENetPeer*> m_Peers;
|
||||
std::vector<CNetServerSession*> m_Sessions;
|
||||
|
||||
/**
|
||||
* Loads game properties into the specified message
|
||||
*
|
||||
* @param pMessage Message where to load game properties
|
||||
*/
|
||||
void BuildGameSetupMessage( CGameSetupMessage* pMessage );
|
||||
std::vector<std::pair<CNetServerSession*, CNetMessage*> > m_LocalMessageQueue;
|
||||
|
||||
/**
|
||||
* Loads player slot properties into the specified message
|
||||
*
|
||||
* @param pMessage Message where to load player properties
|
||||
* @param pPlayerSlot Player slot properties
|
||||
*/
|
||||
void BuildPlayerSlotAssignmentMessage(
|
||||
CAssignPlayerSlotMessage* pMessage,
|
||||
CPlayerSlot* pPlayerSlot );
|
||||
NetServerState m_State;
|
||||
|
||||
/**
|
||||
* Callback function used by the BuildGameSetupMessage to iterate over the
|
||||
* game properties. It will be called for each property of the game
|
||||
*
|
||||
* @param name Property name
|
||||
* @param pProperty Pointer to game property
|
||||
* @param pData Context pointer passed on iteration startup
|
||||
*/
|
||||
static void GameSetupMessageCallback(
|
||||
const CStrW& name,
|
||||
ISynchedJSProperty *pProperty,
|
||||
void *pData );
|
||||
CStrW m_ServerName;
|
||||
CStrW m_WelcomeMessage;
|
||||
int m_Port;
|
||||
|
||||
/**
|
||||
* Retrieves a free session ID from the recycled sessions list
|
||||
*
|
||||
* @return Free session ID
|
||||
*/
|
||||
uint GetFreeSessionID( void ) const;
|
||||
|
||||
IDSessionMap m_IDSessions; // List of connected ID and session pairs
|
||||
|
||||
public:
|
||||
|
||||
void SetPlayerPassword ( const CStr& password );
|
||||
CStrW GetPlayerName ( void ) const { return m_PlayerName; }
|
||||
NetServerState GetState ( void ) const { return m_State; }
|
||||
int StartGame ( void );
|
||||
static void ScriptingInit ( void );
|
||||
|
||||
protected:
|
||||
|
||||
// Assign a session ID to the session. Do this just before calling AddSession
|
||||
void AssignSessionID( CNetSession* pSession );
|
||||
|
||||
// Call the JS callback for incoming events
|
||||
void OnPlayerChat ( const CStrW& from, const CStrW& message );
|
||||
virtual void OnPlayerJoin ( CNetSession* pSession );
|
||||
virtual void OnPlayerLeave ( CNetSession* pSession );
|
||||
void SetupPlayer ( CNetSession* pSession );
|
||||
|
||||
//static bool OnPlayerJoin ( void* pContext, CFsmEvent* pEvent );
|
||||
static bool OnError ( void* pContext, CFsmEvent* pEvent );
|
||||
static bool OnHandshake ( void* pContext, CFsmEvent* pEvent );
|
||||
static bool OnAuthenticate ( void* pContext, CFsmEvent* pEvent );
|
||||
static bool OnPreGame ( void* pContext, CFsmEvent* pEvent );
|
||||
static bool OnInGame ( void* pContext, CFsmEvent* pEvent );
|
||||
static bool OnChat ( void* pContext, CFsmEvent* pEvent );
|
||||
|
||||
// Ask the server if the session is allowed to start observing.
|
||||
//
|
||||
// Returns:
|
||||
// true if the session should be made an observer
|
||||
// false otherwise
|
||||
virtual bool AllowObserver( CNetSession* pSession );
|
||||
|
||||
public:
|
||||
CGame* m_Game; // Pointer to actual game
|
||||
|
||||
protected:
|
||||
CGameAttributes* m_GameAttributes; // Stores game attributes
|
||||
|
||||
private:
|
||||
CJSMap< IDSessionMap > m_JsSessions;
|
||||
|
||||
/*
|
||||
All sessions that have observer status (observer as in watcher - simple
|
||||
chatters don't have an entry here, only in m_Sessions).
|
||||
Sessions are added here after they have successfully requested observer
|
||||
status.
|
||||
*/
|
||||
SessionList m_Observers;
|
||||
uint m_MaxObservers; // Maximum number of observers
|
||||
NetServerState m_State; // Holds server state
|
||||
CStrW m_Name; // Server name
|
||||
CStrW m_WelcomeMessage; // Nice welcome message
|
||||
CStrW m_PlayerName; // Player name
|
||||
CStrW m_PlayerPassword; // Player password
|
||||
int m_Port; // The listening port
|
||||
CScriptObject m_OnChat;
|
||||
CScriptObject m_OnClientConnect;
|
||||
CScriptObject m_OnClientDisconnect;
|
||||
|
||||
static void AttributeUpdate ( const CStrW& name, const CStrW& newValue, void* pData);
|
||||
static void PlayerAttributeUpdate ( const CStrW& name, const CStrW& value, CPlayer* pPlayer, void* pData );
|
||||
static void PlayerSlotAssignment ( void* pData, CPlayerSlot* pPlayerSlot );
|
||||
u32 m_NextHostID;
|
||||
|
||||
CNetServerTurnManager* m_ServerTurnManager;
|
||||
};
|
||||
|
||||
/// Global network server for the standard game
|
||||
extern CNetServer *g_NetServer;
|
||||
|
||||
#endif // NETSERVER_H
|
||||
|
@ -17,65 +17,228 @@
|
||||
|
||||
#include "precompiled.h"
|
||||
#include "NetSession.h"
|
||||
#include "NetClient.h"
|
||||
#include "NetServer.h"
|
||||
#include "NetMessage.h"
|
||||
#include "ps/CLogger.h"
|
||||
#include "scriptinterface/ScriptInterface.h"
|
||||
|
||||
static const uint INVALID_SESSION = 0;
|
||||
#include <enet/enet.h>
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: CNetSession()
|
||||
// Desc: Constructor
|
||||
//-----------------------------------------------------------------------------
|
||||
CNetSession::CNetSession(CNetHost* pHost, ENetPeer* pPeer)
|
||||
static const int CHANNEL_COUNT = 1;
|
||||
|
||||
|
||||
CNetClientSession::CNetClientSession(CNetClient& client) :
|
||||
m_Client(client)
|
||||
{
|
||||
m_Host = pHost;
|
||||
m_Peer = pPeer;
|
||||
m_ID = INVALID_SESSION;
|
||||
m_PlayerSlot = NULL;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: ~CNetSession()
|
||||
// Desc: Destructor
|
||||
//-----------------------------------------------------------------------------
|
||||
CNetSession::~CNetSession()
|
||||
CNetClientSession::~CNetClientSession()
|
||||
{
|
||||
m_Peer = NULL;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: SetName()
|
||||
// Desc: Set a new name for the session
|
||||
//-----------------------------------------------------------------------------
|
||||
void CNetSession::SetName(const CStr& name)
|
||||
|
||||
|
||||
CNetClientSessionRemote::CNetClientSessionRemote(CNetClient& client) :
|
||||
CNetClientSession(client), m_Host(NULL), m_Server(NULL)
|
||||
{
|
||||
m_Name = name;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: SetID()
|
||||
// Desc: Set new ID for this session
|
||||
//-----------------------------------------------------------------------------
|
||||
void CNetSession::SetID(uint ID)
|
||||
CNetClientSessionRemote::~CNetClientSessionRemote()
|
||||
{
|
||||
m_ID = ID;
|
||||
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: SetPlayerSlot()
|
||||
// Desc: Set the player slot for this session
|
||||
//-----------------------------------------------------------------------------
|
||||
void CNetSession::SetPlayerSlot(CPlayerSlot* pPlayerSlot)
|
||||
bool CNetClientSessionRemote::Connect(u16 port, const CStr& server)
|
||||
{
|
||||
m_PlayerSlot = pPlayerSlot;
|
||||
debug_assert(!m_Host);
|
||||
debug_assert(!m_Server);
|
||||
|
||||
// Create ENet host
|
||||
ENetHost* host = enet_host_create(NULL, 1, 0, 0);
|
||||
if (!host)
|
||||
return false;
|
||||
|
||||
// Bind to specified host
|
||||
ENetAddress addr;
|
||||
addr.port = port;
|
||||
if (enet_address_set_host(&addr, server.c_str()) < 0)
|
||||
return false;
|
||||
|
||||
// Initiate connection to server
|
||||
ENetPeer* peer = enet_host_connect(host, &addr, CHANNEL_COUNT);
|
||||
if (!peer)
|
||||
return false;
|
||||
|
||||
m_Host = host;
|
||||
m_Server = peer;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: ScriptingInit()
|
||||
// Desc:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CNetSession::ScriptingInit()
|
||||
void CNetClientSessionRemote::Disconnect()
|
||||
{
|
||||
AddProperty(L"id", &CNetSession::m_ID);
|
||||
AddProperty(L"name", &CNetSession::m_Name);
|
||||
debug_assert(m_Host && m_Server);
|
||||
|
||||
CJSObject<CNetSession>::ScriptingInit("NetSession");
|
||||
// TODO: ought to do reliable async disconnects, probably
|
||||
enet_peer_disconnect_now(m_Server, 0);
|
||||
enet_host_destroy(m_Host);
|
||||
|
||||
m_Host = NULL;
|
||||
m_Server = NULL;
|
||||
}
|
||||
|
||||
void CNetClientSessionRemote::Poll()
|
||||
{
|
||||
debug_assert(m_Host && m_Server);
|
||||
|
||||
ENetEvent event;
|
||||
while (enet_host_service(m_Host, &event, 0) > 0)
|
||||
{
|
||||
switch (event.type)
|
||||
{
|
||||
case ENET_EVENT_TYPE_CONNECT:
|
||||
{
|
||||
debug_assert(event.peer == m_Server);
|
||||
|
||||
// Report the server address
|
||||
char hostname[256] = "(error)";
|
||||
enet_address_get_host_ip(&event.peer->address, hostname, ARRAY_SIZE(hostname));
|
||||
LOGMESSAGE(L"Net client: Connected to %hs:%u", hostname, event.peer->address.port);
|
||||
|
||||
GetClient().HandleConnect();
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ENET_EVENT_TYPE_DISCONNECT:
|
||||
{
|
||||
debug_assert(event.peer == m_Server);
|
||||
|
||||
GetClient().HandleDisconnect();
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ENET_EVENT_TYPE_RECEIVE:
|
||||
{
|
||||
CNetMessage* msg = CNetMessageFactory::CreateMessage(event.packet->data, event.packet->dataLength, GetClient().GetScriptInterface());
|
||||
if (msg)
|
||||
{
|
||||
LOGMESSAGE(L"Net client: Received message %hs of size %lu from server", msg->ToString().c_str(), (unsigned long)msg->GetSerializedLength());
|
||||
|
||||
bool ok = GetClient().HandleMessage(msg);
|
||||
debug_assert(ok); // TODO
|
||||
|
||||
delete msg;
|
||||
}
|
||||
|
||||
enet_packet_destroy(event.packet);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool CNetClientSessionRemote::SendMessage(const CNetMessage* message)
|
||||
{
|
||||
debug_assert(m_Host && m_Server);
|
||||
|
||||
return CNetHost::SendMessage(message, m_Server, "server");
|
||||
}
|
||||
|
||||
|
||||
|
||||
CNetClientSessionLocal::CNetClientSessionLocal(CNetClient& client, CNetServer& server) :
|
||||
CNetClientSession(client), m_Server(server), m_ServerSession(NULL)
|
||||
{
|
||||
server.AddLocalClientSession(*this);
|
||||
client.HandleConnect();
|
||||
}
|
||||
|
||||
void CNetClientSessionLocal::Poll()
|
||||
{
|
||||
for (size_t i = 0; i < m_LocalMessageQueue.size(); ++i)
|
||||
{
|
||||
CNetMessage* msg = m_LocalMessageQueue[i];
|
||||
|
||||
LOGMESSAGE(L"Net client: Received local message %hs of size %lu from server", msg->ToString().c_str(), (unsigned long)msg->GetSerializedLength());
|
||||
|
||||
bool ok = GetClient().HandleMessage(msg);
|
||||
debug_assert(ok); // TODO
|
||||
|
||||
delete msg;
|
||||
}
|
||||
m_LocalMessageQueue.clear();
|
||||
}
|
||||
|
||||
void CNetClientSessionLocal::Disconnect()
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
bool CNetClientSessionLocal::SendMessage(const CNetMessage* message)
|
||||
{
|
||||
LOGMESSAGE(L"Net client: Sending local message %hs to server", message->ToString().c_str());
|
||||
m_Server.SendLocalMessage(*this, message);
|
||||
return true;
|
||||
}
|
||||
|
||||
void CNetClientSessionLocal::AddLocalMessage(const CNetMessage* message)
|
||||
{
|
||||
// Clone into the client's script context
|
||||
CNetMessage* clonedMessage = CNetMessageFactory::CloneMessage(message, GetClient().GetScriptInterface());
|
||||
if (!clonedMessage)
|
||||
return;
|
||||
m_LocalMessageQueue.push_back(clonedMessage);
|
||||
}
|
||||
|
||||
|
||||
|
||||
CNetServerSession::CNetServerSession(CNetServer& server) :
|
||||
m_Server(server)
|
||||
{
|
||||
}
|
||||
|
||||
CNetServerSession::~CNetServerSession()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
CNetServerSessionRemote::CNetServerSessionRemote(CNetServer& server, ENetPeer* peer) :
|
||||
CNetServerSession(server), m_Peer(peer)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void CNetServerSessionRemote::Disconnect()
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
bool CNetServerSessionRemote::SendMessage(const CNetMessage* message)
|
||||
{
|
||||
return GetServer().SendMessage(m_Peer, message);
|
||||
}
|
||||
|
||||
|
||||
|
||||
CNetServerSessionLocal::CNetServerSessionLocal(CNetServer& server, CNetClientSessionLocal& clientSession) :
|
||||
CNetServerSession(server), m_ClientSession(clientSession)
|
||||
{
|
||||
}
|
||||
|
||||
void CNetServerSessionLocal::Disconnect()
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
bool CNetServerSessionLocal::SendMessage(const CNetMessage* message)
|
||||
{
|
||||
LOGMESSAGE(L"Net server: Sending local message %hs to %p", message->ToString().c_str(), &m_ClientSession);
|
||||
m_ClientSession.AddLocalMessage(message);
|
||||
return true;
|
||||
}
|
||||
|
@ -18,90 +18,171 @@
|
||||
#ifndef NETSESSION_H
|
||||
#define NETSESSION_H
|
||||
|
||||
#include "fsm.h"
|
||||
#include "scripting/ScriptableObject.h"
|
||||
#include "network/fsm.h"
|
||||
#include "network/NetHost.h"
|
||||
#include "ps/CStr.h"
|
||||
#include "scriptinterface/ScriptVal.h"
|
||||
|
||||
class CPlayerSlot;
|
||||
class CNetHost;
|
||||
class CNetSession;
|
||||
typedef struct _ENetPeer ENetPeer;
|
||||
class CNetClient;
|
||||
class CNetServer;
|
||||
|
||||
struct FsmActionCtx
|
||||
class CNetServerSessionLocal; // forward declaration, needed because of circular references
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Network client/server sessions.
|
||||
*
|
||||
* Each session has two classes: CNetClientSession runs on the client,
|
||||
* and CNetServerSession runs on the server.
|
||||
* A client runs one session at once; a server typically runs many.
|
||||
*
|
||||
* There are two variants of each session: Remote (the normal ENet-based
|
||||
* network session) and Local (a shortcut when the client and server are
|
||||
* running inside the same process and don't need the network to communicate).
|
||||
*/
|
||||
|
||||
/**
|
||||
* The client end of a network session.
|
||||
* Provides an abstraction of the network interface, allowing communication with the server.
|
||||
*/
|
||||
class CNetClientSession
|
||||
{
|
||||
CNetHost* pHost;
|
||||
CNetSession* pSession;
|
||||
NONCOPYABLE(CNetClientSession);
|
||||
|
||||
public:
|
||||
CNetClientSession(CNetClient& client);
|
||||
virtual ~CNetClientSession();
|
||||
|
||||
virtual void Poll() = 0;
|
||||
virtual void Disconnect() = 0;
|
||||
virtual bool SendMessage(const CNetMessage* message) = 0;
|
||||
|
||||
CNetClient& GetClient() { return m_Client; }
|
||||
|
||||
private:
|
||||
CNetClient& m_Client;
|
||||
};
|
||||
|
||||
/**
|
||||
* CNetSession is a wrapper class around the ENet peer concept
|
||||
* which represents a peer from a network connection. A
|
||||
* network session is spawned by CNetServer each time a
|
||||
* client connects and destroyed when it disconnects. When a
|
||||
* new message is received from a client, its representing
|
||||
* session object's message handler is called for processing
|
||||
* that message.
|
||||
* CNetSession is also a state machine. All client requests
|
||||
* are delegated to the current state. The current
|
||||
* CNetSessionState object's methods will change the current
|
||||
* state as appropriate.
|
||||
* ENet-based implementation of CNetClientSession.
|
||||
*/
|
||||
|
||||
class CNetSession : public CFsm,
|
||||
public CJSObject<CNetSession>
|
||||
class CNetClientSessionRemote : public CNetClientSession
|
||||
{
|
||||
NONCOPYABLE(CNetSession);
|
||||
|
||||
friend class CNetHost;
|
||||
NONCOPYABLE(CNetClientSessionRemote);
|
||||
|
||||
public:
|
||||
CNetClientSessionRemote(CNetClient& client);
|
||||
~CNetClientSessionRemote();
|
||||
|
||||
virtual ~CNetSession();
|
||||
bool Connect(u16 port, const CStr& server);
|
||||
|
||||
/**
|
||||
* Retrieves the name of the session
|
||||
*
|
||||
* @return Session name
|
||||
*/
|
||||
const CStrW& GetName() const { return m_Name; }
|
||||
virtual void Poll();
|
||||
virtual void Disconnect();
|
||||
virtual bool SendMessage(const CNetMessage* message);
|
||||
|
||||
/**
|
||||
* Set the new name for the session
|
||||
*
|
||||
* @param name The session new name
|
||||
*/
|
||||
void SetName(const CStr& name);
|
||||
|
||||
/**
|
||||
* Retrieves the ID of the session
|
||||
*
|
||||
* @return Session ID
|
||||
*/
|
||||
uint GetID() const { return m_ID; }
|
||||
|
||||
/**
|
||||
* Set the ID for this session
|
||||
*
|
||||
* @param New session ID
|
||||
*/
|
||||
void SetID(uint ID);
|
||||
|
||||
FsmActionCtx* GetFsmActionCtx() { return &m_FsmActionCtx; }
|
||||
|
||||
void SetPlayerSlot(CPlayerSlot* pPlayerSlot);
|
||||
CPlayerSlot* GetPlayerSlot() { return m_PlayerSlot; }
|
||||
static void ScriptingInit();
|
||||
ENetPacket* CreatePacket(const CNetMessage* message);
|
||||
|
||||
private:
|
||||
ENetHost* m_Host;
|
||||
ENetPeer* m_Server;
|
||||
};
|
||||
|
||||
// Only the hosts can create sessions
|
||||
CNetSession(CNetHost* pHost, ENetPeer* pPeer);
|
||||
/**
|
||||
* Local implementation of CNetClientSession, for use with servers
|
||||
* running in the same process.
|
||||
*/
|
||||
class CNetClientSessionLocal : public CNetClientSession
|
||||
{
|
||||
NONCOPYABLE(CNetClientSessionLocal);
|
||||
|
||||
CNetHost* m_Host; // The associated local host
|
||||
ENetPeer* m_Peer; // Represents the peer host
|
||||
uint m_ID; // Session ID
|
||||
CStrW m_Name; // Session name
|
||||
CPlayerSlot* m_PlayerSlot;
|
||||
FsmActionCtx m_FsmActionCtx;
|
||||
public:
|
||||
CNetClientSessionLocal(CNetClient& client, CNetServer& server);
|
||||
|
||||
void SetServerSession(CNetServerSessionLocal* session) { m_ServerSession = session; }
|
||||
CNetServerSessionLocal* GetServerSession() { return m_ServerSession; }
|
||||
|
||||
virtual void Poll();
|
||||
virtual void Disconnect();
|
||||
virtual bool SendMessage(const CNetMessage* message);
|
||||
|
||||
void AddLocalMessage(const CNetMessage* message);
|
||||
|
||||
private:
|
||||
CNetServer& m_Server;
|
||||
CNetServerSessionLocal* m_ServerSession;
|
||||
|
||||
std::vector<CNetMessage*> m_LocalMessageQueue;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* The server's end of a network session.
|
||||
* Represents an abstraction of the state of the client, storing all the per-client data
|
||||
* needed by the server.
|
||||
*/
|
||||
class CNetServerSession : public CFsm
|
||||
{
|
||||
NONCOPYABLE(CNetServerSession);
|
||||
|
||||
public:
|
||||
CNetServerSession(CNetServer& server);
|
||||
virtual ~CNetServerSession();
|
||||
|
||||
CNetServer& GetServer() { return m_Server; }
|
||||
|
||||
const CStr& GetGUID() const { return m_GUID; }
|
||||
void SetGUID(const CStr& guid) { m_GUID = guid; }
|
||||
|
||||
const CStrW& GetUserName() const { return m_UserName; }
|
||||
void SetUserName(const CStrW& name) { m_UserName = name; }
|
||||
|
||||
u32 GetHostID() const { return m_HostID; }
|
||||
void SetHostID(u32 id) { m_HostID = id; }
|
||||
|
||||
virtual void Disconnect() = 0;
|
||||
virtual bool SendMessage(const CNetMessage* message) = 0;
|
||||
|
||||
private:
|
||||
CNetServer& m_Server;
|
||||
|
||||
CStr m_GUID;
|
||||
CStrW m_UserName;
|
||||
u32 m_HostID;
|
||||
};
|
||||
|
||||
/**
|
||||
* ENet-based implementation of CNetServerSession.
|
||||
*/
|
||||
class CNetServerSessionRemote : public CNetServerSession
|
||||
{
|
||||
NONCOPYABLE(CNetServerSessionRemote);
|
||||
|
||||
public:
|
||||
CNetServerSessionRemote(CNetServer& server, ENetPeer* peer);
|
||||
|
||||
virtual void Disconnect();
|
||||
virtual bool SendMessage(const CNetMessage* message);
|
||||
|
||||
private:
|
||||
ENetPeer* m_Peer;
|
||||
};
|
||||
|
||||
/**
|
||||
* Local implementation of CNetServerSession, for use with clients
|
||||
* running in the same process.
|
||||
*/
|
||||
class CNetServerSessionLocal : public CNetServerSession
|
||||
{
|
||||
NONCOPYABLE(CNetServerSessionLocal);
|
||||
|
||||
public:
|
||||
CNetServerSessionLocal(CNetServer& server, CNetClientSessionLocal& clientSession);
|
||||
|
||||
virtual void Disconnect();
|
||||
virtual bool SendMessage(const CNetMessage* message);
|
||||
|
||||
private:
|
||||
CNetClientSessionLocal& m_ClientSession;
|
||||
};
|
||||
|
||||
#endif // NETSESSION_H
|
||||
|
@ -19,11 +19,14 @@
|
||||
|
||||
#include "NetTurnManager.h"
|
||||
|
||||
#include "NetServer.h"
|
||||
#include "NetClient.h"
|
||||
#include "network/NetServer.h"
|
||||
#include "network/NetClient.h"
|
||||
#include "network/NetMessage.h"
|
||||
|
||||
#include "gui/GUIManager.h"
|
||||
#include "maths/MathUtil.h"
|
||||
#include "ps/Profile.h"
|
||||
#include "ps/Pyrogenesis.h"
|
||||
#include "simulation2/Simulation2.h"
|
||||
|
||||
static const int TURN_LENGTH = 200; // TODO: this should be a variable controlled by the server depending on latency
|
||||
@ -41,9 +44,9 @@ static std::string Hexify(const std::string& s)
|
||||
return str.str();
|
||||
}
|
||||
|
||||
CNetTurnManager::CNetTurnManager(CSimulation2& simulation, int playerId, int clientId) :
|
||||
CNetTurnManager::CNetTurnManager(CSimulation2& simulation, int clientId) :
|
||||
m_Simulation2(simulation), m_CurrentTurn(0), m_ReadyTurn(1), m_DeltaTime(0),
|
||||
m_PlayerId(playerId), m_ClientId(clientId), m_HasSyncError(false)
|
||||
m_PlayerId(-1), m_ClientId(clientId), m_HasSyncError(false)
|
||||
{
|
||||
// When we are on turn n, we schedule new commands for n+2.
|
||||
// We know that all other clients have finished scheduling commands for n (else we couldn't have got here).
|
||||
@ -54,6 +57,11 @@ CNetTurnManager::CNetTurnManager(CSimulation2& simulation, int playerId, int cli
|
||||
m_QueuedCommands.resize(COMMAND_DELAY + 1);
|
||||
}
|
||||
|
||||
void CNetTurnManager::SetPlayerID(int playerId)
|
||||
{
|
||||
m_PlayerId = playerId;
|
||||
}
|
||||
|
||||
bool CNetTurnManager::Update(float frameLength)
|
||||
{
|
||||
m_DeltaTime += frameLength;
|
||||
@ -186,8 +194,7 @@ void CNetClientTurnManager::PostCommand(CScriptValRooted data)
|
||||
|
||||
// Transmit command to server
|
||||
CSimulationMessage msg(m_Simulation2.GetScriptInterface(), m_ClientId, m_PlayerId, m_CurrentTurn + COMMAND_DELAY, data.get());
|
||||
CNetSession* session = m_NetClient.GetSession(0);
|
||||
m_NetClient.SendMessage(session, &msg);
|
||||
m_NetClient.SendMessage(&msg);
|
||||
|
||||
// Add to our local queue
|
||||
//AddCommand(m_ClientId, m_PlayerId, data, m_CurrentTurn + COMMAND_DELAY);
|
||||
@ -204,8 +211,7 @@ void CNetClientTurnManager::NotifyFinishedOwnCommands(u32 turn)
|
||||
CEndCommandBatchMessage msg;
|
||||
msg.m_TurnLength = TURN_LENGTH;
|
||||
msg.m_Turn = turn;
|
||||
CNetSession* session = m_NetClient.GetSession(0);
|
||||
m_NetClient.SendMessage(session, &msg);
|
||||
m_NetClient.SendMessage(&msg);
|
||||
}
|
||||
|
||||
void CNetClientTurnManager::NotifyFinishedUpdate(u32 turn, const std::string& hash)
|
||||
@ -218,8 +224,7 @@ void CNetClientTurnManager::NotifyFinishedUpdate(u32 turn, const std::string& ha
|
||||
CSyncCheckMessage msg;
|
||||
msg.m_Turn = turn;
|
||||
msg.m_Hash = hash;
|
||||
CNetSession* session = m_NetClient.GetSession(0);
|
||||
m_NetClient.SendMessage(session, &msg);
|
||||
m_NetClient.SendMessage(&msg);
|
||||
}
|
||||
|
||||
void CNetClientTurnManager::OnSimulationMessage(CSimulationMessage* msg)
|
||||
@ -230,36 +235,33 @@ void CNetClientTurnManager::OnSimulationMessage(CSimulationMessage* msg)
|
||||
|
||||
|
||||
|
||||
void CNetServerTurnManager::PostCommand(CScriptValRooted data)
|
||||
void CNetLocalTurnManager::PostCommand(CScriptValRooted data)
|
||||
{
|
||||
#ifdef NETTURN_LOG
|
||||
NETTURN_LOG(L"PostCommand()\n");
|
||||
#endif
|
||||
|
||||
// Transmit command to all clients
|
||||
CSimulationMessage msg(m_Simulation2.GetScriptInterface(), m_ClientId, m_PlayerId, m_CurrentTurn + COMMAND_DELAY, data.get());
|
||||
m_NetServer.Broadcast(&msg);
|
||||
|
||||
// Add to our local queue
|
||||
AddCommand(m_ClientId, m_PlayerId, data, m_CurrentTurn + COMMAND_DELAY);
|
||||
// Add directly to the next turn, ignoring COMMAND_DELAY,
|
||||
// because we don't need to compensate for network latency
|
||||
AddCommand(m_ClientId, m_PlayerId, data, m_CurrentTurn + 1);
|
||||
}
|
||||
|
||||
void CNetServerTurnManager::NotifyFinishedOwnCommands(u32 turn)
|
||||
void CNetLocalTurnManager::NotifyFinishedOwnCommands(u32 turn)
|
||||
{
|
||||
#ifdef NETTURN_LOG
|
||||
NETTURN_LOG(L"NotifyFinishedOwnCommands(%d)\n", turn);
|
||||
#endif
|
||||
|
||||
NotifyFinishedClientCommands(m_ClientId, turn);
|
||||
FinishedAllCommands(turn);
|
||||
}
|
||||
|
||||
void CNetServerTurnManager::NotifyFinishedUpdate(u32 turn, const std::string& hash)
|
||||
void CNetLocalTurnManager::NotifyFinishedUpdate(u32 UNUSED(turn), const std::string& UNUSED(hash))
|
||||
{
|
||||
#ifdef NETTURN_LOG
|
||||
NETTURN_LOG(L"NotifyFinishedUpdate(%d, %s)\n", turn, Hexify(hash).c_str());
|
||||
#endif
|
||||
}
|
||||
|
||||
NotifyFinishedClientUpdate(m_ClientId, turn, hash);
|
||||
void CNetLocalTurnManager::OnSimulationMessage(CSimulationMessage* UNUSED(msg))
|
||||
{
|
||||
debug_warn(L"This should never be called");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
CNetServerTurnManager::CNetServerTurnManager(CNetServer& server) :
|
||||
m_NetServer(server), m_ReadyTurn(1)
|
||||
{
|
||||
}
|
||||
|
||||
void CNetServerTurnManager::NotifyFinishedClientCommands(int client, u32 turn)
|
||||
@ -291,8 +293,7 @@ void CNetServerTurnManager::NotifyFinishedClientCommands(int client, u32 turn)
|
||||
msg.m_Turn = turn;
|
||||
m_NetServer.Broadcast(&msg);
|
||||
|
||||
// Move ourselves to the next turn
|
||||
FinishedAllCommands(m_ReadyTurn + 1);
|
||||
m_ReadyTurn = turn;
|
||||
}
|
||||
|
||||
void CNetServerTurnManager::NotifyFinishedClientUpdate(int client, u32 turn, const std::string& hash)
|
||||
@ -318,7 +319,7 @@ void CNetServerTurnManager::NotifyFinishedClientUpdate(int client, u32 turn, con
|
||||
break;
|
||||
|
||||
// Assume the host is correct (maybe we should choose the most common instead to help debugging)
|
||||
std::string expected = it->second[m_ClientId];
|
||||
std::string expected = it->second.begin()->second;
|
||||
|
||||
for (std::map<int, std::string>::iterator cit = it->second.begin(); cit != it->second.end(); ++cit)
|
||||
{
|
||||
@ -335,9 +336,6 @@ void CNetServerTurnManager::NotifyFinishedClientUpdate(int client, u32 turn, con
|
||||
msg.m_HashExpected = expected;
|
||||
m_NetServer.Broadcast(&msg);
|
||||
|
||||
// Process it ourselves
|
||||
OnSyncError(it->first, expected);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -355,37 +353,3 @@ void CNetServerTurnManager::InitialiseClient(int client)
|
||||
|
||||
// TODO: do we need some kind of UninitialiseClient in case they leave?
|
||||
}
|
||||
|
||||
void CNetServerTurnManager::OnSimulationMessage(CSimulationMessage* msg)
|
||||
{
|
||||
// Send it back to all clients immediately
|
||||
m_NetServer.Broadcast(msg);
|
||||
|
||||
// TODO: we should do some validation of ownership (clients can't send commands on behalf of opposing players)
|
||||
|
||||
// TODO: we shouldn't send the message back to the client that first sent it
|
||||
|
||||
// Process it ourselves
|
||||
AddCommand(msg->m_Client, msg->m_Player, msg->m_Data, msg->m_Turn);
|
||||
}
|
||||
|
||||
void CNetLocalTurnManager::PostCommand(CScriptValRooted data)
|
||||
{
|
||||
// Add directly to the next turn, ignoring COMMAND_DELAY,
|
||||
// because we don't need to compensate for network latency
|
||||
AddCommand(m_ClientId, m_PlayerId, data, m_CurrentTurn + 1);
|
||||
}
|
||||
|
||||
void CNetLocalTurnManager::NotifyFinishedOwnCommands(u32 turn)
|
||||
{
|
||||
FinishedAllCommands(turn);
|
||||
}
|
||||
|
||||
void CNetLocalTurnManager::NotifyFinishedUpdate(u32 UNUSED(turn), const std::string& UNUSED(hash))
|
||||
{
|
||||
}
|
||||
|
||||
void CNetLocalTurnManager::OnSimulationMessage(CSimulationMessage* UNUSED(msg))
|
||||
{
|
||||
debug_warn(L"This should never be called");
|
||||
}
|
||||
|
@ -32,10 +32,10 @@ class CSimulation2;
|
||||
* Each player performs the simulation for turn N.
|
||||
* User input is translated into commands scheduled for execution in turn N+2 which are
|
||||
* distributed to all other clients.
|
||||
* After a while, a player want to perform the simulation for turn N+1,
|
||||
* After a while, a player wants to perform the simulation for turn N+1,
|
||||
* which first requires that it has all the other clients' commands for turn N+1.
|
||||
* In that case, it does the simulation and tells all the other clients it has finished
|
||||
* sending commands for turn N+2, and it starts sending commands for turn N+3.
|
||||
* In that case, it does the simulation and tells all the other clients (via the server)
|
||||
* it has finished sending commands for turn N+2, and it starts sending commands for turn N+3.
|
||||
*
|
||||
* Commands are redistributed immediately by the server.
|
||||
* To ensure a consistent execution of commands, they are each associated with a
|
||||
@ -43,19 +43,24 @@ class CSimulation2;
|
||||
*/
|
||||
|
||||
/**
|
||||
* Common network turn system (used by clients, servers, and offline games).
|
||||
* Common network turn system (used by clients and offline games).
|
||||
*/
|
||||
class CNetTurnManager
|
||||
{
|
||||
NONCOPYABLE(CNetTurnManager);
|
||||
public:
|
||||
/**
|
||||
* Construct for a given player ID, and a given network session ID.
|
||||
* Construct for a given network session ID.
|
||||
*/
|
||||
CNetTurnManager(CSimulation2& simulation, int playerId, int clientId);
|
||||
CNetTurnManager(CSimulation2& simulation, int clientId);
|
||||
|
||||
virtual ~CNetTurnManager() { }
|
||||
|
||||
/**
|
||||
* Set the current user's player ID, which will be added into command messages.
|
||||
*/
|
||||
void SetPlayerID(int playerId);
|
||||
|
||||
/**
|
||||
* Advance the simulation by a certain time. If this brings us past the current
|
||||
* turn length, the next turn is processed and the function returns true.
|
||||
@ -127,11 +132,14 @@ protected:
|
||||
bool m_HasSyncError;
|
||||
};
|
||||
|
||||
/**
|
||||
* Implementation of CNetTurnManager for network clients.
|
||||
*/
|
||||
class CNetClientTurnManager : public CNetTurnManager
|
||||
{
|
||||
public:
|
||||
CNetClientTurnManager(CSimulation2& simulation, CNetClient& client, int playerId, int clientId) :
|
||||
CNetTurnManager(simulation, playerId, clientId), m_NetClient(client)
|
||||
CNetClientTurnManager(CSimulation2& simulation, CNetClient& client, int clientId) :
|
||||
CNetTurnManager(simulation, clientId), m_NetClient(client)
|
||||
{
|
||||
}
|
||||
|
||||
@ -147,11 +155,14 @@ protected:
|
||||
CNetClient& m_NetClient;
|
||||
};
|
||||
|
||||
class CNetServerTurnManager : public CNetTurnManager
|
||||
/**
|
||||
* Implementation of CNetTurnManager for offline games.
|
||||
*/
|
||||
class CNetLocalTurnManager : public CNetTurnManager
|
||||
{
|
||||
public:
|
||||
CNetServerTurnManager(CSimulation2& simulation, CNetServer& server, int playerId, int clientId) :
|
||||
CNetTurnManager(simulation, playerId, clientId), m_NetServer(server)
|
||||
CNetLocalTurnManager(CSimulation2& simulation) :
|
||||
CNetTurnManager(simulation, 0)
|
||||
{
|
||||
}
|
||||
|
||||
@ -159,6 +170,23 @@ public:
|
||||
|
||||
virtual void PostCommand(CScriptValRooted data);
|
||||
|
||||
protected:
|
||||
virtual void NotifyFinishedOwnCommands(u32 turn);
|
||||
|
||||
virtual void NotifyFinishedUpdate(u32 turn, const std::string& hash);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* The server-side counterpart to CNetClientTurnManager.
|
||||
* Records the turn state of each client, and sends turn advancement messages
|
||||
* when all clients are ready.
|
||||
*/
|
||||
class CNetServerTurnManager
|
||||
{
|
||||
public:
|
||||
CNetServerTurnManager(CNetServer& server);
|
||||
|
||||
void NotifyFinishedClientCommands(int client, u32 turn);
|
||||
|
||||
void NotifyFinishedClientUpdate(int client, u32 turn, const std::string& hash);
|
||||
@ -166,9 +194,8 @@ public:
|
||||
void InitialiseClient(int client);
|
||||
|
||||
protected:
|
||||
virtual void NotifyFinishedOwnCommands(u32 turn);
|
||||
|
||||
virtual void NotifyFinishedUpdate(u32 turn, const std::string& hash);
|
||||
/// The latest turn for which we have received all commands from all clients
|
||||
u32 m_ReadyTurn;
|
||||
|
||||
// Client ID -> ready turn number (the latest turn for which all commands have been received from that client)
|
||||
std::map<int, u32> m_ClientsReady;
|
||||
@ -183,22 +210,4 @@ protected:
|
||||
CNetServer& m_NetServer;
|
||||
};
|
||||
|
||||
class CNetLocalTurnManager : public CNetTurnManager
|
||||
{
|
||||
public:
|
||||
CNetLocalTurnManager(CSimulation2& simulation) :
|
||||
CNetTurnManager(simulation, 1, 0)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void OnSimulationMessage(CSimulationMessage* msg);
|
||||
|
||||
virtual void PostCommand(CScriptValRooted data);
|
||||
|
||||
protected:
|
||||
virtual void NotifyFinishedOwnCommands(u32 turn);
|
||||
|
||||
virtual void NotifyFinishedUpdate(u32 turn, const std::string& hash);
|
||||
};
|
||||
|
||||
#endif // INCLUDED_NETTURNMANAGER
|
||||
|
@ -1,34 +0,0 @@
|
||||
/* Copyright (C) 2010 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 0 A.D. is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "precompiled.h"
|
||||
#include "Network.h"
|
||||
|
||||
CStr CErrorMessage::ToString() const
|
||||
{
|
||||
return CStr("NetErrorMessage: ") + m_Error;
|
||||
}
|
||||
|
||||
CStr CConnectCompleteMessage::ToString() const
|
||||
{
|
||||
return CStr("ConnectCompleteMessage");
|
||||
}
|
||||
|
||||
CStr CCloseRequestMessage::ToString() const
|
||||
{
|
||||
return CStr("CloseRequestMessage");
|
||||
}
|
@ -1,62 +0,0 @@
|
||||
/* Copyright (C) 2010 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 0 A.D. is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef INCLUDED_NETWORK_NETWORK
|
||||
#define INCLUDED_NETWORK_NETWORK
|
||||
|
||||
#include "ps/Pyrogenesis.h"
|
||||
#include "NetMessage.h"
|
||||
|
||||
class CErrorMessage : public CNetMessage
|
||||
{
|
||||
public:
|
||||
const char* m_Error;
|
||||
|
||||
CErrorMessage() :
|
||||
CNetMessage(NMT_ERROR), m_Error(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
CErrorMessage(const char* error) :
|
||||
CNetMessage(NMT_ERROR), m_Error(error)
|
||||
{
|
||||
}
|
||||
|
||||
virtual CStr ToString() const;
|
||||
};
|
||||
|
||||
struct CCloseRequestMessage : public CNetMessage
|
||||
{
|
||||
CCloseRequestMessage() :
|
||||
CNetMessage(NMT_CLOSE_REQUEST)
|
||||
{
|
||||
}
|
||||
|
||||
virtual CStr ToString() const;
|
||||
};
|
||||
|
||||
struct CConnectCompleteMessage : public CNetMessage
|
||||
{
|
||||
CConnectCompleteMessage() :
|
||||
CNetMessage(NMT_CONNECT_COMPLETE)
|
||||
{
|
||||
}
|
||||
|
||||
virtual CStr ToString() const;
|
||||
};
|
||||
|
||||
#endif
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2009 Wildfire Games.
|
||||
/* Copyright (C) 2010 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2009 Wildfire Games.
|
||||
/* Copyright (C) 2010 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
@ -15,14 +15,6 @@
|
||||
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
*-----------------------------------------------------------------------------
|
||||
* FILE : fsm.cpp
|
||||
* PROJECT : 0 A.D.
|
||||
* DESCRIPTION : Finite state machine class implementation
|
||||
*-----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
// INCLUDES
|
||||
#include "precompiled.h"
|
||||
#include "fsm.h"
|
||||
@ -306,7 +298,7 @@ CFsmTransition* CFsm::AddTransition(
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: AddTransition()
|
||||
// Desc: Adds a new transistion to the state machine
|
||||
// Desc: Adds a new transition to the state machine
|
||||
//-----------------------------------------------------------------------------
|
||||
CFsmTransition* CFsm::AddTransition(
|
||||
unsigned int state,
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2009 Wildfire Games.
|
||||
/* Copyright (C) 2010 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
@ -15,14 +15,6 @@
|
||||
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
*-----------------------------------------------------------------------------
|
||||
* FILE : fsm.h
|
||||
* PROJECT : 0 A.D.
|
||||
* DESCRIPTION : Finite state machine class definitions
|
||||
*-----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#ifndef FSM_H
|
||||
#define FSM_H
|
||||
|
||||
@ -53,14 +45,11 @@ typedef std::map< unsigned int, CFsmEvent* > EventMap;
|
||||
typedef std::vector< CFsmTransition* > TransitionList;
|
||||
typedef std::vector< CallbackFunction > CallbackList;
|
||||
|
||||
/*
|
||||
CLASS : CFsmEvent
|
||||
DESCRIPTION : CFsmEvent class represents a signal in the state machine
|
||||
that a change has occurred.
|
||||
NOTES : The CFsmEvent objects are under the control of CFsm so
|
||||
they are created and deleted via CFsm.
|
||||
/**
|
||||
* Represents a signal in the state machine that a change has occurred.
|
||||
* The CFsmEvent objects are under the control of CFsm so
|
||||
* they are created and deleted via CFsm.
|
||||
*/
|
||||
|
||||
class CFsmEvent
|
||||
{
|
||||
NONCOPYABLE(CFsmEvent);
|
||||
@ -79,13 +68,9 @@ private:
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
CLASS : CFsmTransition
|
||||
DESCRIPTION : The CFsmTransition class is an association of event, condition,
|
||||
action and next state.
|
||||
NOTES :
|
||||
/**
|
||||
* An association of event, condition, action and next state.
|
||||
*/
|
||||
|
||||
class CFsmTransition
|
||||
{
|
||||
NONCOPYABLE(CFsmTransition);
|
||||
@ -118,18 +103,17 @@ private:
|
||||
CallbackList m_Conditions; // List of conditions for transition
|
||||
};
|
||||
|
||||
/*
|
||||
CLASS : CFsm
|
||||
DESCRIPTION : CFsm manages states, events, actions and transitions
|
||||
between states. It provides an interface for advertising
|
||||
events and track the current state. The implementation is
|
||||
a Mealy state machine, so the system respond to events
|
||||
and execute some action.
|
||||
NOTES : A Mealy state machine has behaviour associated with state
|
||||
transitions; Mealy machines are event driven where an
|
||||
event triggers a state transition
|
||||
/**
|
||||
* Manages states, events, actions and transitions
|
||||
* between states. It provides an interface for advertising
|
||||
* events and track the current state. The implementation is
|
||||
* a Mealy state machine, so the system respond to events
|
||||
* and execute some action.
|
||||
*
|
||||
* A Mealy state machine has behaviour associated with state
|
||||
* transitions; Mealy machines are event driven where an
|
||||
* event triggers a state transition
|
||||
*/
|
||||
|
||||
class CFsm
|
||||
{
|
||||
NONCOPYABLE(CFsm);
|
||||
@ -141,13 +125,11 @@ public:
|
||||
/**
|
||||
* Constructs the state machine. This method must be overriden so that
|
||||
* connections are constructed for the particular state machine implemented
|
||||
*
|
||||
*/
|
||||
virtual void Setup( void );
|
||||
|
||||
/**
|
||||
* Clear event, action and condition lists and reset state machine
|
||||
*
|
||||
*/
|
||||
void Shutdown( void );
|
||||
|
||||
@ -177,8 +159,9 @@ public:
|
||||
bool IsValidEvent ( unsigned int eventType ) const;
|
||||
virtual bool IsDone ( void ) const;
|
||||
|
||||
private:
|
||||
protected:
|
||||
void SetCurrState ( unsigned int state );
|
||||
private:
|
||||
bool IsFirstTime ( void ) const;
|
||||
|
||||
bool m_Done; // FSM work is done
|
||||
|
@ -20,67 +20,14 @@
|
||||
#include "lib/external_libraries/sdl.h"
|
||||
#include "network/NetServer.h"
|
||||
#include "network/NetClient.h"
|
||||
#include "ps/ConfigDB.h"
|
||||
#include "network/NetTurnManager.h"
|
||||
#include "ps/CLogger.h"
|
||||
#include "ps/Game.h"
|
||||
#include "ps/Filesystem.h"
|
||||
#include "ps/GameAttributes.h"
|
||||
#include "ps/Loader.h"
|
||||
#include "ps/XML/Xeromyces.h"
|
||||
#include "scriptinterface/ScriptInterface.h"
|
||||
|
||||
class TestNetServer : public CNetServer
|
||||
{
|
||||
public:
|
||||
TestNetServer(ScriptInterface& scriptInterface, CGameAttributes& gameAttributes) :
|
||||
CNetServer(scriptInterface, NULL, &gameAttributes), m_HasConnection(false)
|
||||
{
|
||||
}
|
||||
|
||||
bool m_HasConnection;
|
||||
|
||||
protected:
|
||||
virtual void OnPlayerJoin(CNetSession* pSession)
|
||||
{
|
||||
debug_printf(L"# player joined\n");
|
||||
|
||||
for (size_t slot = 0; slot < m_GameAttributes->GetSlotCount(); ++slot)
|
||||
{
|
||||
if (m_GameAttributes->GetSlot(slot)->GetAssignment() == SLOT_OPEN)
|
||||
{
|
||||
debug_printf(L"# assigning slot %d\n", slot);
|
||||
m_GameAttributes->GetSlot(slot)->AssignToSession(pSession);
|
||||
m_HasConnection = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual void OnPlayerLeave(CNetSession* UNUSED(pSession))
|
||||
{
|
||||
debug_printf(L"# player left\n");
|
||||
}
|
||||
};
|
||||
|
||||
class TestNetClient : public CNetClient
|
||||
{
|
||||
public:
|
||||
TestNetClient(ScriptInterface& scriptInterface, CGameAttributes& gameAttributes) :
|
||||
CNetClient(scriptInterface, NULL, &gameAttributes), m_HasConnection(false)
|
||||
{
|
||||
}
|
||||
|
||||
bool m_HasConnection;
|
||||
|
||||
protected:
|
||||
virtual void OnConnectComplete()
|
||||
{
|
||||
debug_printf(L"# connect complete\n");
|
||||
m_HasConnection = true;
|
||||
}
|
||||
|
||||
virtual void OnStartGame()
|
||||
{
|
||||
debug_printf(L"# start game\n");
|
||||
}
|
||||
};
|
||||
#include "simulation2/Simulation2.h"
|
||||
|
||||
class TestNetComms : public CxxTest::TestSuite
|
||||
{
|
||||
@ -91,41 +38,38 @@ public:
|
||||
TS_ASSERT_OK(g_VFS->Mount(L"", DataDir()/L"mods/public", VFS_MOUNT_MUST_EXIST));
|
||||
TS_ASSERT_OK(g_VFS->Mount(L"cache", DataDir()/L"_testcache"));
|
||||
CXeromyces::Startup();
|
||||
|
||||
new ScriptingHost;
|
||||
CGameAttributes::ScriptingInit();
|
||||
CNetServer::ScriptingInit();
|
||||
CNetClient::ScriptingInit();
|
||||
|
||||
new CConfigDB;
|
||||
}
|
||||
|
||||
void tearDown()
|
||||
{
|
||||
delete &g_ConfigDB;
|
||||
|
||||
CGameAttributes::ScriptingShutdown();
|
||||
CNetServer::ScriptingShutdown();
|
||||
CNetClient::ScriptingShutdown();
|
||||
delete &g_ScriptingHost;
|
||||
|
||||
CXeromyces::Terminate();
|
||||
g_VFS.reset();
|
||||
DeleteDirectory(DataDir()/L"_testcache");
|
||||
}
|
||||
|
||||
void connect(TestNetServer& server, TestNetClient& client)
|
||||
bool clients_are_all(const std::vector<CNetClient*>& clients, uint state)
|
||||
{
|
||||
TS_ASSERT(server.Start(NULL, 0, NULL));
|
||||
TS_ASSERT(client.Create());
|
||||
TS_ASSERT(client.ConnectAsync("127.0.0.1", DEFAULT_HOST_PORT));
|
||||
for (size_t j = 0; j < clients.size(); ++j)
|
||||
if (clients[j]->GetCurrState() != state)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void connect(CNetServer& server, const std::vector<CNetClient*>& clients)
|
||||
{
|
||||
TS_ASSERT(server.SetupConnection());
|
||||
clients[0]->SetupLocalConnection(server);
|
||||
for (size_t j = 1; j < clients.size(); ++j)
|
||||
TS_ASSERT(clients[j]->SetupConnection("127.0.0.1"));
|
||||
|
||||
for (size_t i = 0; ; ++i)
|
||||
{
|
||||
debug_printf(L".");
|
||||
// debug_printf(L".");
|
||||
server.Poll();
|
||||
client.Poll();
|
||||
for (size_t j = 0; j < clients.size(); ++j)
|
||||
clients[j]->Poll();
|
||||
|
||||
if (server.m_HasConnection && client.m_HasConnection)
|
||||
if (server.GetState() == SERVER_STATE_PREGAME && clients_are_all(clients, NCS_PREGAME))
|
||||
break;
|
||||
|
||||
if (i > 20)
|
||||
@ -138,28 +82,102 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void disconnect(CNetServer& server, const std::vector<CNetClient*>& clients)
|
||||
{
|
||||
for (size_t i = 0; ; ++i)
|
||||
{
|
||||
// debug_printf(L".");
|
||||
server.Poll();
|
||||
for (size_t j = 0; j < clients.size(); ++j)
|
||||
clients[j]->Poll();
|
||||
|
||||
if (server.GetState() == SERVER_STATE_UNCONNECTED && clients_are_all(clients, NCS_UNCONNECTED))
|
||||
break;
|
||||
|
||||
if (i > 20)
|
||||
{
|
||||
TS_FAIL("disconnection timeout");
|
||||
break;
|
||||
}
|
||||
|
||||
SDL_Delay(100);
|
||||
}
|
||||
}
|
||||
|
||||
void wait(CNetServer& server, const std::vector<CNetClient*>& clients, size_t msecs)
|
||||
{
|
||||
for (size_t i = 0; i < msecs/10; ++i)
|
||||
{
|
||||
server.Poll();
|
||||
for (size_t j = 0; j < clients.size(); ++j)
|
||||
clients[j]->Poll();
|
||||
|
||||
SDL_Delay(10);
|
||||
}
|
||||
}
|
||||
|
||||
void test_basic_DISABLED()
|
||||
{
|
||||
ScriptInterface scriptInterface("Engine");
|
||||
// This doesn't actually test much, it just runs a very quick multiplayer game
|
||||
// and prints a load of debug output so you can see if anything funny's going on
|
||||
|
||||
CGameAttributes gameAttributesServer;
|
||||
CGameAttributes gameAttributesClient;
|
||||
TestNetServer server(scriptInterface, gameAttributesServer);
|
||||
TestNetClient client(scriptInterface, gameAttributesClient);
|
||||
connect(server, client);
|
||||
client.CNetHost::Shutdown();
|
||||
server.CNetHost::Shutdown();
|
||||
TestStdoutLogger logger;
|
||||
|
||||
std::vector<CNetClient*> clients;
|
||||
|
||||
CGame client1Game(true);
|
||||
CGame client2Game(true);
|
||||
CGame client3Game(true);
|
||||
|
||||
CNetServer server;
|
||||
|
||||
CScriptValRooted attrs;
|
||||
server.GetScriptInterface().Eval("({map:'Latium',thing:'example'})", attrs);
|
||||
server.UpdateGameAttributes(attrs);
|
||||
|
||||
CNetClient client1(&client1Game);
|
||||
CNetClient client2(&client2Game);
|
||||
CNetClient client3(&client3Game);
|
||||
|
||||
clients.push_back(&client1);
|
||||
clients.push_back(&client2);
|
||||
clients.push_back(&client3);
|
||||
|
||||
connect(server, clients);
|
||||
debug_printf(L"%ls", client1.TestReadGuiMessages().c_str());
|
||||
|
||||
server.StartGame();
|
||||
server.Poll();
|
||||
SDL_Delay(100);
|
||||
for (size_t j = 0; j < clients.size(); ++j)
|
||||
{
|
||||
clients[j]->Poll();
|
||||
TS_ASSERT_OK(LDR_NonprogressiveLoad());
|
||||
clients[j]->LoadFinished();
|
||||
}
|
||||
|
||||
void TODO_test_destructor()
|
||||
{
|
||||
ScriptInterface scriptInterface("Engine");
|
||||
wait(server, clients, 100);
|
||||
|
||||
CGameAttributes gameAttributesServer;
|
||||
CGameAttributes gameAttributesClient;
|
||||
TestNetServer server(scriptInterface, gameAttributesServer);
|
||||
TestNetClient client(scriptInterface, gameAttributesClient);
|
||||
connect(server, client);
|
||||
// run in Valgrind; this shouldn't leak
|
||||
{
|
||||
CScriptValRooted cmd;
|
||||
client1.GetScriptInterface().Eval("({type:'debug-print', message:'[>>> client1 test sim command]\\n'})", cmd);
|
||||
client1Game.GetTurnManager()->PostCommand(cmd);
|
||||
}
|
||||
|
||||
{
|
||||
CScriptValRooted cmd;
|
||||
client2.GetScriptInterface().Eval("({type:'debug-print', message:'[>>> client2 test sim command]\\n'})", cmd);
|
||||
client2Game.GetTurnManager()->PostCommand(cmd);
|
||||
}
|
||||
|
||||
wait(server, clients, 100);
|
||||
client1Game.GetTurnManager()->Update(1.0f);
|
||||
client2Game.GetTurnManager()->Update(1.0f);
|
||||
client3Game.GetTurnManager()->Update(1.0f);
|
||||
wait(server, clients, 100);
|
||||
client1Game.GetTurnManager()->Update(1.0f);
|
||||
client2Game.GetTurnManager()->Update(1.0f);
|
||||
client3Game.GetTurnManager()->Update(1.0f);
|
||||
wait(server, clients, 100);
|
||||
}
|
||||
};
|
||||
|
@ -719,23 +719,10 @@ void CConsole::SaveHistory()
|
||||
|
||||
void CConsole::SendChatMessage(const wchar_t *pText)
|
||||
{
|
||||
CNetHost *pHost = NULL;
|
||||
|
||||
if ( pHost )
|
||||
if (g_NetClient)
|
||||
{
|
||||
CChatMessage chat;
|
||||
chat.m_Recipient = CHAT_RECIPIENT_ALL;
|
||||
chat.m_Message = pText;
|
||||
|
||||
if ( pHost->IsServer() )
|
||||
{
|
||||
CNetServer* pServer = ( CNetServer* )pHost;
|
||||
|
||||
chat.m_Sender = 0;
|
||||
ReceivedChatMessage( pServer->GetPlayerName(), chat.m_Message.c_str() );
|
||||
}
|
||||
|
||||
pHost->Broadcast( &chat );
|
||||
// TODO
|
||||
// g_NetClient3->SendChatMessage(pText);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -136,6 +136,8 @@ CLogger::~CLogger ()
|
||||
void CLogger::WriteMessage(const wchar_t* message)
|
||||
{
|
||||
++m_NumberOfMessages;
|
||||
// if (m_UseDebugPrintf)
|
||||
// debug_printf(L"MESSAGE: %ls\n", message);
|
||||
|
||||
*m_MainLog << L"<p>" << message << L"</p>\n";
|
||||
m_MainLog->flush();
|
||||
@ -354,7 +356,6 @@ void CLogger::PushRenderMessage(ELogMethod method, const wchar_t* message)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CLogger::CleanupRenderQueue()
|
||||
{
|
||||
if (m_RenderMessages.empty())
|
||||
@ -397,3 +398,15 @@ std::wstring TestLogger::GetOutput()
|
||||
{
|
||||
return m_Stream.str();
|
||||
}
|
||||
|
||||
TestStdoutLogger::TestStdoutLogger()
|
||||
{
|
||||
m_OldLogger = g_Logger;
|
||||
g_Logger = new CLogger(&std::wcout, &blackHoleStream, false, false);
|
||||
}
|
||||
|
||||
TestStdoutLogger::~TestStdoutLogger()
|
||||
{
|
||||
delete g_Logger;
|
||||
g_Logger = m_OldLogger;
|
||||
}
|
||||
|
@ -132,4 +132,17 @@ private:
|
||||
std::wstringstream m_Stream;
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper class for unit tests - redirects all log output to stdout.
|
||||
*/
|
||||
class TestStdoutLogger
|
||||
{
|
||||
NONCOPYABLE(TestStdoutLogger);
|
||||
public:
|
||||
TestStdoutLogger();
|
||||
~TestStdoutLogger();
|
||||
private:
|
||||
CLogger* m_OldLogger;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -29,20 +29,23 @@
|
||||
#include "graphics/UnitManager.h"
|
||||
#include "lib/timer.h"
|
||||
#include "network/NetClient.h"
|
||||
#include "network/NetServer.h"
|
||||
#include "network/NetTurnManager.h"
|
||||
#include "ps/CConsole.h"
|
||||
#include "ps/CLogger.h"
|
||||
#include "ps/CStr.h"
|
||||
#include "ps/GameAttributes.h"
|
||||
#include "ps/Loader.h"
|
||||
#include "ps/Overlay.h"
|
||||
#include "ps/Profile.h"
|
||||
#include "ps/World.h"
|
||||
#include "scripting/ScriptingHost.h"
|
||||
#include "scriptinterface/ScriptInterface.h"
|
||||
#include "simulation2/Simulation2.h"
|
||||
#include "simulation2/components/ICmpPlayer.h"
|
||||
#include "simulation2/components/ICmpPlayerManager.h"
|
||||
|
||||
#include "gui/GUIManager.h"
|
||||
|
||||
class CNetServer;
|
||||
extern CNetServer *g_NetServer;
|
||||
|
||||
extern bool g_GameRestarted;
|
||||
|
||||
/**
|
||||
@ -54,17 +57,18 @@ CGame *g_Game=NULL;
|
||||
* Constructor
|
||||
*
|
||||
**/
|
||||
CGame::CGame():
|
||||
CGame::CGame(bool disableGraphics):
|
||||
m_World(new CWorld(this)),
|
||||
m_Simulation2(new CSimulation2(&m_World->GetUnitManager(), m_World->GetTerrain())),
|
||||
m_GameView(new CGameView(this)),
|
||||
m_pLocalPlayer(NULL),
|
||||
m_GameView(disableGraphics ? NULL : new CGameView(this)),
|
||||
m_GameStarted(false),
|
||||
m_Paused(false),
|
||||
m_SimRate(1.0f)
|
||||
m_SimRate(1.0f),
|
||||
m_PlayerID(-1)
|
||||
{
|
||||
// Need to set the CObjectManager references after various objects have
|
||||
// been initialised, so do it here rather than via the initialisers above.
|
||||
if (m_GameView)
|
||||
m_World->GetUnitManager().SetObjectManager(m_GameView->GetObjectManager());
|
||||
|
||||
m_TurnManager = new CNetLocalTurnManager(*m_Simulation2); // this will get replaced if we're a net server/client
|
||||
@ -83,6 +87,7 @@ CGame::CGame():
|
||||
CGame::~CGame()
|
||||
{
|
||||
// Again, the in-game call tree is going to be different to the main menu one.
|
||||
if (CProfileManager::IsInitialised())
|
||||
g_Profiler.StructuralReset();
|
||||
|
||||
delete m_TurnManager;
|
||||
@ -95,7 +100,11 @@ void CGame::SetTurnManager(CNetTurnManager* turnManager)
|
||||
{
|
||||
if (m_TurnManager)
|
||||
delete m_TurnManager;
|
||||
|
||||
m_TurnManager = turnManager;
|
||||
|
||||
if (m_TurnManager)
|
||||
m_TurnManager->SetPlayerID(m_PlayerID);
|
||||
}
|
||||
|
||||
|
||||
@ -103,12 +112,13 @@ void CGame::SetTurnManager(CNetTurnManager* turnManager)
|
||||
* Initializes the game with the set of attributes provided.
|
||||
* Makes calls to initialize the game view, world, and simulation objects.
|
||||
* Calls are made to facilitate progress reporting of the initialization.
|
||||
*
|
||||
* @param CGameAttributes * pAttribs pointer to the game attribute values
|
||||
* @return PSRETURN 0
|
||||
**/
|
||||
PSRETURN CGame::RegisterInit(CGameAttributes* pAttribs)
|
||||
void CGame::RegisterInit(const CScriptValRooted& attribs)
|
||||
{
|
||||
std::wstring mapFile;
|
||||
m_Simulation2->GetScriptInterface().GetProperty(attribs.get(), "map", mapFile);
|
||||
mapFile += L".pmp";
|
||||
|
||||
LDR_BeginRegistering();
|
||||
|
||||
// RC, 040804 - GameView needs to be initialized before World, otherwise GameView initialization
|
||||
@ -116,11 +126,12 @@ PSRETURN CGame::RegisterInit(CGameAttributes* pAttribs)
|
||||
// values. At the minute, it's just lighting settings, but could be extended to store camera position.
|
||||
// Storing lighting settings in the game view seems a little odd, but it's no big deal; maybe move it at
|
||||
// some point to be stored in the world object?
|
||||
m_GameView->RegisterInit(pAttribs);
|
||||
m_World->RegisterInit(pAttribs);
|
||||
if (m_GameView)
|
||||
m_GameView->RegisterInit();
|
||||
m_World->RegisterInit(mapFile);
|
||||
LDR_EndRegistering();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Game initialization has been completed. Set game started flag and start the session.
|
||||
*
|
||||
@ -129,7 +140,7 @@ PSRETURN CGame::RegisterInit(CGameAttributes* pAttribs)
|
||||
PSRETURN CGame::ReallyStartGame()
|
||||
{
|
||||
// Call the reallyStartGame GUI function, but only if it exists
|
||||
if (g_GUI->HasPages())
|
||||
if (g_GUI && g_GUI->HasPages())
|
||||
{
|
||||
jsval fval, rval;
|
||||
JSBool ok = JS_GetProperty(g_ScriptingHost.getContext(), g_GUI->GetScriptObject(), "reallyStartGame", &fval);
|
||||
@ -138,10 +149,14 @@ PSRETURN CGame::ReallyStartGame()
|
||||
ok = JS_CallFunctionValue(g_ScriptingHost.getContext(), g_GUI->GetScriptObject(), fval, 0, NULL, &rval);
|
||||
}
|
||||
|
||||
if (g_NetClient)
|
||||
g_NetClient->LoadFinished();
|
||||
|
||||
debug_printf(L"GAME STARTED, ALL INIT COMPLETE\n");
|
||||
m_GameStarted=true;
|
||||
|
||||
// The call tree we've built for pregame probably isn't useful in-game.
|
||||
if (CProfileManager::IsInitialised())
|
||||
g_Profiler.StructuralReset();
|
||||
|
||||
// Mark terrain as modified so the minimap can repaint (is there a cleaner way of handling this?)
|
||||
@ -150,56 +165,37 @@ PSRETURN CGame::ReallyStartGame()
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare to start the game.
|
||||
* Set up the players list then call RegisterInit that initializes the game and is used to report progress.
|
||||
*
|
||||
* @param CGameAttributes * pGameAttributes game attributes for initialization.
|
||||
* @return PSRETURN 0 if successful,
|
||||
* error information if not.
|
||||
**/
|
||||
PSRETURN CGame::StartGame(CGameAttributes *pAttribs)
|
||||
void CGame::ChangeNetStatus(ENetStatus status)
|
||||
{
|
||||
try
|
||||
if (g_GUI && g_GUI->HasPages())
|
||||
{
|
||||
// JW: this loop is taken from ScEd and fixes lack of player color.
|
||||
// TODO: determine proper number of players.
|
||||
// SB: Only do this for non-network games
|
||||
if (!g_NetClient && !g_NetServer)
|
||||
const char* statusStr = "?";
|
||||
switch (status)
|
||||
{
|
||||
for (int i=1; i<8; ++i)
|
||||
pAttribs->GetSlot(i)->AssignLocal();
|
||||
case NET_WAITING_FOR_CONNECT: statusStr = "waiting_for_connect"; break;
|
||||
case NET_NORMAL: statusStr = "normal"; break;
|
||||
}
|
||||
|
||||
pAttribs->FinalizeSlots();
|
||||
m_NumPlayers=pAttribs->GetSlotCount();
|
||||
g_GUI->GetScriptInterface().CallFunctionVoid(OBJECT_TO_JSVAL(g_GUI->GetScriptObject()), "changeNetStatus", statusStr);
|
||||
}
|
||||
}
|
||||
|
||||
// Player 0 = Gaia - allocate one extra
|
||||
m_Players.resize(m_NumPlayers + 1);
|
||||
|
||||
for (size_t i=0;i <= m_NumPlayers;i++)
|
||||
m_Players[i]=pAttribs->GetPlayer(i);
|
||||
|
||||
if (g_NetClient)
|
||||
int CGame::GetPlayerID()
|
||||
{
|
||||
// TODO
|
||||
m_pLocalPlayer = g_NetClient->GetLocalPlayer();
|
||||
debug_assert(m_pLocalPlayer && "Darn it! We weren't assigned to a slot!");
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pLocalPlayer=m_Players[1];
|
||||
return m_PlayerID;
|
||||
}
|
||||
|
||||
RegisterInit(pAttribs);
|
||||
}
|
||||
catch (PSERROR_Game& e)
|
||||
void CGame::SetPlayerID(int playerID)
|
||||
{
|
||||
return e.getCode();
|
||||
}
|
||||
return 0;
|
||||
m_PlayerID = playerID;
|
||||
if (m_TurnManager)
|
||||
m_TurnManager->SetPlayerID(m_PlayerID);
|
||||
}
|
||||
|
||||
void CGame::StartGame(const CScriptValRooted& attribs)
|
||||
{
|
||||
RegisterInit(attribs);
|
||||
}
|
||||
|
||||
// TODO: doInterpolate is optional because Atlas interpolates explicitly,
|
||||
// so that it has more control over the update rate. The game might want to
|
||||
@ -219,6 +215,9 @@ bool CGame::Update(double deltaTime, bool doInterpolate)
|
||||
if (m_Paused)
|
||||
return true;
|
||||
|
||||
if (!m_TurnManager)
|
||||
return true;
|
||||
|
||||
deltaTime *= m_SimRate;
|
||||
|
||||
bool ok = true;
|
||||
@ -240,6 +239,9 @@ bool CGame::Update(double deltaTime, bool doInterpolate)
|
||||
|
||||
void CGame::Interpolate(float frameLength)
|
||||
{
|
||||
if (!m_TurnManager)
|
||||
return;
|
||||
|
||||
m_TurnManager->Interpolate(frameLength);
|
||||
}
|
||||
|
||||
@ -312,32 +314,15 @@ void CGame::EndGame()
|
||||
break;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Get the player object from the players list at the provided index.
|
||||
*
|
||||
* @param idx sequential position in the list.
|
||||
* @return CPlayer * pointer to player requested.
|
||||
**/
|
||||
CPlayer *CGame::GetPlayer(size_t idx)
|
||||
{
|
||||
if (idx > m_NumPlayers)
|
||||
{
|
||||
// debug_warn(L"Invalid player ID");
|
||||
// LOG(CLogger::Error, "", "Invalid player ID %d (outside 0..%d)", idx, m_NumPlayers);
|
||||
return m_Players[0];
|
||||
}
|
||||
// Be a bit more paranoid - maybe m_Players hasn't been set large enough
|
||||
else if (idx >= m_Players.size())
|
||||
{
|
||||
debug_warn(L"Invalid player ID");
|
||||
LOG(CLogger::Error, L"", L"Invalid player ID %lu (not <=%lu - internal error?)", (unsigned long)idx, (unsigned long)m_Players.size());
|
||||
|
||||
if (m_Players.size() != 0)
|
||||
return m_Players[0];
|
||||
else
|
||||
return NULL; // the caller will probably crash because of this,
|
||||
// but at least we've reported the error
|
||||
}
|
||||
else
|
||||
return m_Players[idx];
|
||||
static CColor BrokenColor(0.3f, 0.3f, 0.3f, 1.0f);
|
||||
CColor CGame::GetPlayerColour(int player) const
|
||||
{
|
||||
CmpPtr<ICmpPlayerManager> cmpPlayerManager(*m_Simulation2, SYSTEM_ENTITY);
|
||||
if (cmpPlayerManager.null())
|
||||
return BrokenColor;
|
||||
CmpPtr<ICmpPlayer> cmpPlayer(*m_Simulation2, cmpPlayerManager->GetPlayerByID(player));
|
||||
if (cmpPlayer.null())
|
||||
return BrokenColor;
|
||||
return cmpPlayer->GetColour();
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2009 Wildfire Games.
|
||||
/* Copyright (C) 2010 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
@ -30,15 +30,9 @@
|
||||
class CWorld;
|
||||
class CSimulation2;
|
||||
class CGameView;
|
||||
class CPlayer;
|
||||
class CGameAttributes;
|
||||
class CNetTurnManager;
|
||||
|
||||
/**
|
||||
* Default player limit (not counting the Gaia player)
|
||||
* This may be overridden by system.cfg ("max_players")
|
||||
**/
|
||||
#define PS_MAX_PLAYERS 8
|
||||
class CScriptValRooted;
|
||||
struct CColor;
|
||||
|
||||
/**
|
||||
* The container that holds the rules, resources and attributes of the game.
|
||||
@ -61,18 +55,6 @@ class CGame
|
||||
* pointer to the CGameView object representing the view into the game world.
|
||||
**/
|
||||
CGameView *m_GameView;
|
||||
/**
|
||||
* pointer to the local CPlayer object operating on the game world.
|
||||
**/
|
||||
CPlayer *m_pLocalPlayer;
|
||||
/**
|
||||
* STL vectors of pointers to all CPlayer objects(including gaia) operating on the game world.
|
||||
**/
|
||||
std::vector<CPlayer *> m_Players;
|
||||
/**
|
||||
* number of players operating on the game world(not including gaia).
|
||||
**/
|
||||
size_t m_NumPlayers;
|
||||
/**
|
||||
* the game has been initialized and ready for use if true.
|
||||
**/
|
||||
@ -81,6 +63,9 @@ class CGame
|
||||
* scale multiplier for simulation rate.
|
||||
**/
|
||||
float m_SimRate;
|
||||
|
||||
int m_PlayerID;
|
||||
|
||||
/**
|
||||
* enumerated values for game status.
|
||||
**/
|
||||
@ -96,7 +81,13 @@ class CGame
|
||||
CNetTurnManager* m_TurnManager;
|
||||
|
||||
public:
|
||||
CGame();
|
||||
enum ENetStatus
|
||||
{
|
||||
NET_WAITING_FOR_CONNECT, /// we have loaded the game; waiting for other players to finish loading
|
||||
NET_NORMAL /// running the game
|
||||
};
|
||||
|
||||
CGame(bool disableGraphics = false);
|
||||
~CGame();
|
||||
|
||||
/**
|
||||
@ -104,15 +95,14 @@ public:
|
||||
**/
|
||||
bool m_Paused;
|
||||
|
||||
/*
|
||||
Initialize all local state and members for playing a game described by
|
||||
the attribute class, and start the game.
|
||||
|
||||
Return: 0 on OK - a PSRETURN code otherwise
|
||||
*/
|
||||
PSRETURN StartGame(CGameAttributes *pGameAttributes);
|
||||
void StartGame(const CScriptValRooted& attribs);
|
||||
PSRETURN ReallyStartGame();
|
||||
|
||||
/**
|
||||
* Notify the game of changes in the network connection status.
|
||||
*/
|
||||
void ChangeNetStatus(ENetStatus status);
|
||||
|
||||
/*
|
||||
Perform all per-frame updates
|
||||
*/
|
||||
@ -123,41 +113,10 @@ public:
|
||||
void UpdateGameStatus();
|
||||
void EndGame();
|
||||
|
||||
/**
|
||||
* Get pointer to the local player object.
|
||||
*
|
||||
* @return CPlayer * the value of m_pLocalPlayer.
|
||||
**/
|
||||
inline CPlayer *GetLocalPlayer()
|
||||
{ return m_pLocalPlayer; }
|
||||
/**
|
||||
* Change the pointer to the local player object.
|
||||
*
|
||||
* @param CPlayer * pLocalPlayer pointer to a valid player object.
|
||||
**/
|
||||
inline void SetLocalPlayer(CPlayer *pLocalPlayer)
|
||||
{ m_pLocalPlayer=pLocalPlayer; }
|
||||
int GetPlayerID();
|
||||
void SetPlayerID(int playerID);
|
||||
|
||||
// PT: No longer inline, because it does too much error checking. When
|
||||
// everything stops trying to access players before they're loaded, feel
|
||||
// free to put the inline version back.
|
||||
CPlayer *GetPlayer(size_t idx);
|
||||
|
||||
/**
|
||||
* Get a reference to the m_Players vector.
|
||||
*
|
||||
* @return std::vector<CPlayer*> * reference to m_Players.
|
||||
**/
|
||||
inline std::vector<CPlayer*>* GetPlayers()
|
||||
{ return( &m_Players ); }
|
||||
|
||||
/**
|
||||
* Get m_NumPlayers.
|
||||
*
|
||||
* @return the number of players (not including gaia)
|
||||
**/
|
||||
inline size_t GetNumPlayers() const
|
||||
{ return m_NumPlayers; }
|
||||
CColor GetPlayerColour(int player) const;
|
||||
|
||||
/**
|
||||
* Get m_GameStarted.
|
||||
@ -216,12 +175,10 @@ public:
|
||||
void SetTurnManager(CNetTurnManager* turnManager);
|
||||
|
||||
CNetTurnManager* GetTurnManager() const
|
||||
{
|
||||
return m_TurnManager;
|
||||
}
|
||||
{ return m_TurnManager; }
|
||||
|
||||
private:
|
||||
PSRETURN RegisterInit(CGameAttributes* pAttribs);
|
||||
void RegisterInit(const CScriptValRooted& attribs);
|
||||
};
|
||||
|
||||
extern CGame *g_Game;
|
||||
|
@ -1,436 +0,0 @@
|
||||
/* Copyright (C) 2009 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 0 A.D. is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "precompiled.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include "GameAttributes.h"
|
||||
#include "Game.h"
|
||||
#include "ConfigDB.h"
|
||||
#include "network/NetSession.h"
|
||||
#include "CLogger.h"
|
||||
#include "ps/XML/Xeromyces.h"
|
||||
#include "simulation/LOSManager.h"
|
||||
|
||||
CGameAttributes *g_GameAttributes;
|
||||
|
||||
CPlayerSlot::CPlayerSlot(size_t slotID, CPlayer *pPlayer):
|
||||
m_SlotID(slotID),
|
||||
m_Assignment(SLOT_OPEN),
|
||||
m_pSession(NULL),
|
||||
m_SessionID(0),
|
||||
m_pPlayer(pPlayer),
|
||||
m_Callback(NULL)
|
||||
{
|
||||
//AddProperty(L"session", (GetFn)&CPlayerSlot::JSI_GetSession);
|
||||
AddLocalProperty(L"session", &m_pSession, true );
|
||||
AddLocalProperty(L"player", &m_pPlayer, true );
|
||||
}
|
||||
|
||||
CPlayerSlot::~CPlayerSlot()
|
||||
{}
|
||||
|
||||
void CPlayerSlot::ScriptingInit()
|
||||
{
|
||||
AddMethod<bool, &CPlayerSlot::JSI_AssignClosed>("assignClosed", 0);
|
||||
AddMethod<bool, &CPlayerSlot::JSI_AssignToSession>("assignToSession", 1);
|
||||
AddMethod<bool, &CPlayerSlot::JSI_AssignLocal>("assignLocal", 0);
|
||||
AddMethod<bool, &CPlayerSlot::JSI_AssignOpen>("assignOpen", 0);
|
||||
AddProperty(L"assignment", &CPlayerSlot::JSI_GetAssignment);
|
||||
// AddMethod<bool, &CPlayerSlot::JSI_AssignAI>("assignAI", <num_args>);
|
||||
|
||||
CJSObject<CPlayerSlot>::ScriptingInit("PlayerSlot");
|
||||
}
|
||||
|
||||
jsval CPlayerSlot::JSI_GetSession(JSContext* UNUSED(cx))
|
||||
{
|
||||
if (m_pSession)
|
||||
return OBJECT_TO_JSVAL(m_pSession->GetScript());
|
||||
else
|
||||
return JSVAL_NULL;
|
||||
}
|
||||
|
||||
jsval CPlayerSlot::JSI_GetAssignment(JSContext* UNUSED(cx))
|
||||
{
|
||||
switch (m_Assignment)
|
||||
{
|
||||
case SLOT_CLOSED:
|
||||
return g_ScriptingHost.UCStringToValue(L"closed");
|
||||
case SLOT_OPEN:
|
||||
return g_ScriptingHost.UCStringToValue(L"open");
|
||||
case SLOT_SESSION:
|
||||
return g_ScriptingHost.UCStringToValue(L"session");
|
||||
/* case SLOT_AI:*/
|
||||
default:
|
||||
return INT_TO_JSVAL(m_Assignment);
|
||||
}
|
||||
}
|
||||
|
||||
bool CPlayerSlot::JSI_AssignClosed(JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv))
|
||||
{
|
||||
AssignClosed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CPlayerSlot::JSI_AssignOpen(JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv))
|
||||
{
|
||||
AssignOpen();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CPlayerSlot::JSI_AssignToSession(JSContext* UNUSED(cx), uintN argc, jsval* argv)
|
||||
{
|
||||
if (argc != 1)
|
||||
return false;
|
||||
CNetSession* pSession = ToNative<CNetSession>(argv[0]);
|
||||
if (pSession)
|
||||
{
|
||||
AssignToSession(pSession);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CPlayerSlot::JSI_AssignLocal(JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv))
|
||||
{
|
||||
AssignToSessionID(1);
|
||||
return true;
|
||||
}
|
||||
|
||||
void CPlayerSlot::CallCallback()
|
||||
{
|
||||
if (m_Callback)
|
||||
m_Callback(m_CallbackData, this);
|
||||
}
|
||||
|
||||
void CPlayerSlot::SetAssignment(EPlayerSlotAssignment assignment,
|
||||
CNetSession *pSession, int sessionID)
|
||||
{
|
||||
m_Assignment=assignment;
|
||||
m_pSession=pSession;
|
||||
m_SessionID=sessionID;
|
||||
|
||||
CallCallback();
|
||||
}
|
||||
|
||||
void CPlayerSlot::AssignClosed()
|
||||
{
|
||||
SetAssignment(SLOT_CLOSED, NULL, -1);
|
||||
}
|
||||
|
||||
void CPlayerSlot::AssignOpen()
|
||||
{
|
||||
SetAssignment(SLOT_OPEN, NULL, -1);
|
||||
}
|
||||
|
||||
void CPlayerSlot::AssignToSession(CNetSession *pSession)
|
||||
{
|
||||
SetAssignment(SLOT_SESSION, pSession, pSession->GetID());
|
||||
m_pPlayer->SetName(pSession->GetName());
|
||||
}
|
||||
|
||||
void CPlayerSlot::AssignToSessionID(int id)
|
||||
{
|
||||
SetAssignment(SLOT_SESSION, NULL, id);
|
||||
}
|
||||
|
||||
void CPlayerSlot::AssignLocal()
|
||||
{
|
||||
AssignToSessionID(1);
|
||||
}
|
||||
|
||||
namespace PlayerSlotArray_JS
|
||||
{
|
||||
JSBool GetProperty( JSContext* cx, JSObject* obj, jsval id, jsval* vp )
|
||||
{
|
||||
CGameAttributes *pInstance=(CGameAttributes *)JS_GetPrivate(cx, obj);
|
||||
if (!JSVAL_IS_INT(id))
|
||||
return JS_FALSE;
|
||||
size_t index=ToPrimitive<size_t>(id);
|
||||
|
||||
if (index >= pInstance->m_NumSlots)
|
||||
return JS_FALSE;
|
||||
|
||||
*vp=OBJECT_TO_JSVAL(pInstance->m_PlayerSlots[index]->GetScript());
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
JSBool SetProperty( JSContext* UNUSED(cx), JSObject* UNUSED(obj), jsval UNUSED(id), jsval* UNUSED(vp) )
|
||||
{
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
JSClass Class = {
|
||||
"PlayerSlotArray", JSCLASS_HAS_PRIVATE,
|
||||
JS_PropertyStub, JS_PropertyStub,
|
||||
GetProperty, SetProperty,
|
||||
JS_EnumerateStub, JS_ResolveStub,
|
||||
JS_ConvertStub, JS_FinalizeStub
|
||||
};
|
||||
|
||||
JSBool Construct( JSContext* cx, JSObject* obj, uintN argc, jsval* UNUSED(argv), jsval* rval )
|
||||
{
|
||||
if (argc != 0)
|
||||
return JS_FALSE;
|
||||
|
||||
JSObject *newObj=JS_NewObject(cx, &Class, NULL, obj);
|
||||
*rval=OBJECT_TO_JSVAL(newObj);
|
||||
return JS_TRUE;
|
||||
}
|
||||
};
|
||||
|
||||
CGameAttributes::CGameAttributes():
|
||||
m_MapFile("test01.pmp"),
|
||||
m_ResourceLevel("default"),
|
||||
m_StartingPhase("default"),
|
||||
m_LOSSetting(LOS_SETTING_NORMAL),
|
||||
m_FogOfWar(true),
|
||||
m_GameMode("default"),
|
||||
m_ScreenshotMode(false),
|
||||
m_NumSlots(8),
|
||||
m_UpdateCB(NULL),
|
||||
m_PlayerUpdateCB(NULL),
|
||||
m_PlayerSlotAssignmentCB(NULL)
|
||||
{
|
||||
m_PlayerSlotArrayJS=g_ScriptingHost.CreateCustomObject("PlayerSlotArray");
|
||||
JS_AddRoot(g_ScriptingHost.GetContext(), &m_PlayerSlotArrayJS);
|
||||
JS_SetPrivate(g_ScriptingHost.GetContext(), m_PlayerSlotArrayJS, this);
|
||||
|
||||
AddSynchedProperty(L"mapFile", &m_MapFile);
|
||||
AddSynchedProperty(L"resourceLevel", &m_ResourceLevel);
|
||||
AddSynchedProperty(L"startingPhase", &m_StartingPhase);
|
||||
AddSynchedProperty(L"numSlots", &m_NumSlots, &CGameAttributes::OnNumSlotsUpdate);
|
||||
AddSynchedProperty(L"losSetting", &m_LOSSetting);
|
||||
AddSynchedProperty(L"fogOfWar", &m_FogOfWar);
|
||||
AddSynchedProperty(L"gameMode", &m_GameMode);
|
||||
AddSynchedProperty(L"screenshotMode", &m_ScreenshotMode);
|
||||
|
||||
CXeromyces XeroFile;
|
||||
if (XeroFile.Load(L"temp/players.xml") != PSRETURN_OK)
|
||||
{
|
||||
LOG(CLogger::Error, L"", L"Failed to load players list (temp/players.xml)");
|
||||
|
||||
// Basic default players
|
||||
|
||||
m_Players.push_back(new CPlayer(0));
|
||||
m_Players.back()->SetName(L"Gaia");
|
||||
m_Players.back()->SetColour(SPlayerColour(1.0f, 1.0f, 1.0f));
|
||||
|
||||
m_Players.push_back(new CPlayer(1));
|
||||
m_Players.back()->SetName(L"Player 1");
|
||||
m_Players.back()->SetColour(SPlayerColour(0.4375f, 0.4375f, 0.8125f));
|
||||
}
|
||||
else
|
||||
{
|
||||
int at_name = XeroFile.GetAttributeID("name");
|
||||
int at_rgb = XeroFile.GetAttributeID("rgb");
|
||||
|
||||
XMBElement root = XeroFile.GetRoot();
|
||||
XERO_ITER_EL(root, player)
|
||||
{
|
||||
XMBAttributeList attr = player.GetAttributes();
|
||||
m_Players.push_back(new CPlayer((int)m_Players.size()));
|
||||
m_Players.back()->SetName(attr.GetNamedItem(at_name));
|
||||
|
||||
std::stringstream str;
|
||||
str << CStr(attr.GetNamedItem(at_rgb));
|
||||
int r, g, b;
|
||||
if (str >> r >> g >> b)
|
||||
m_Players.back()->SetColour(SPlayerColour(r/255.0f, g/255.0f, b/255.0f));
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<CPlayer *>::iterator it=m_Players.begin();
|
||||
++it; // Skip Gaia - gaia doesn't account for a slot
|
||||
for (;it != m_Players.end();++it)
|
||||
{
|
||||
m_PlayerSlots.push_back(new CPlayerSlot((*it)->GetPlayerID()-1, *it));
|
||||
}
|
||||
// The first player is always the server player in MP, or the local player
|
||||
// in SP
|
||||
m_PlayerSlots[0]->AssignToSessionID(1);
|
||||
}
|
||||
|
||||
CGameAttributes::~CGameAttributes()
|
||||
{
|
||||
std::vector<CPlayerSlot *>::iterator it=m_PlayerSlots.begin();
|
||||
while (it != m_PlayerSlots.end())
|
||||
{
|
||||
delete (*it)->GetPlayer();
|
||||
delete *it;
|
||||
++it;
|
||||
}
|
||||
|
||||
// PT: ??? - Gaia isn't a player slot, but still needs to be deleted; but
|
||||
// this feels rather unpleasantly inconsistent with the above code, so maybe
|
||||
// there's a better way to avoid the memory leak.
|
||||
delete m_Players[0];
|
||||
|
||||
JS_RemoveRoot(g_ScriptingHost.GetContext(), &m_PlayerSlotArrayJS);
|
||||
}
|
||||
|
||||
void CGameAttributes::ScriptingInit()
|
||||
{
|
||||
g_ScriptingHost.DefineCustomObjectType(&PlayerSlotArray_JS::Class,
|
||||
PlayerSlotArray_JS::Construct, 0, NULL, NULL, NULL, NULL);
|
||||
|
||||
AddMethod<jsval_t, &CGameAttributes::JSI_GetOpenSlot>("getOpenSlot", 0);
|
||||
AddProperty(L"slots", &CGameAttributes::JSI_GetPlayerSlots);
|
||||
AddProperty(L"getUsedSlotsAmount", &CGameAttributes::JSI_GetUsedSlotsAmount);
|
||||
|
||||
CJSObject<CGameAttributes>::ScriptingInit("GameAttributes");
|
||||
}
|
||||
|
||||
jsval_t CGameAttributes::JSI_GetOpenSlot(JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv))
|
||||
{
|
||||
std::vector <CPlayerSlot *>::iterator it;
|
||||
for (it = m_PlayerSlots.begin();it != m_PlayerSlots.end();++it)
|
||||
{
|
||||
if ((*it)->GetAssignment() == SLOT_OPEN)
|
||||
return OBJECT_TO_JSVAL((*it)->GetScript());
|
||||
}
|
||||
return JSVAL_NULL;
|
||||
}
|
||||
|
||||
jsval CGameAttributes::JSI_GetUsedSlotsAmount(JSContext* UNUSED(cx))
|
||||
{
|
||||
int i = 0;
|
||||
std::vector <CPlayerSlot *>::iterator it;
|
||||
for (it = m_PlayerSlots.begin();it != m_PlayerSlots.end();++it)
|
||||
{
|
||||
if ((*it)->GetAssignment() != SLOT_OPEN)
|
||||
{
|
||||
i++;
|
||||
}
|
||||
else
|
||||
{
|
||||
return INT_TO_JSVAL(i);
|
||||
}
|
||||
}
|
||||
return INT_TO_JSVAL(i);
|
||||
}
|
||||
|
||||
jsval CGameAttributes::JSI_GetPlayerSlots(JSContext* UNUSED(cx))
|
||||
{
|
||||
return OBJECT_TO_JSVAL(m_PlayerSlotArrayJS);
|
||||
}
|
||||
|
||||
void CGameAttributes::OnNumSlotsUpdate(CSynchedJSObjectBase *owner)
|
||||
{
|
||||
CGameAttributes *pInstance=(CGameAttributes*)owner;
|
||||
|
||||
// Clamp to a preset upper bound.
|
||||
CConfigValue *val=g_ConfigDB.GetValue(CFG_MOD, "max_players");
|
||||
uint maxPlayers=PS_MAX_PLAYERS;
|
||||
if (val)
|
||||
val->GetUnsignedInt(maxPlayers);
|
||||
if (pInstance->m_NumSlots > maxPlayers)
|
||||
pInstance->m_NumSlots = maxPlayers;
|
||||
|
||||
// Resize array according to new number of slots (note that the array
|
||||
// size will go up to maxPlayers (above), but will never be made smaller -
|
||||
// this to avoid losing player pointers and make sure they are all
|
||||
// reclaimed in the end - it's just simpler that way ;-) )
|
||||
if (pInstance->m_NumSlots > pInstance->m_PlayerSlots.size())
|
||||
{
|
||||
for (size_t i=pInstance->m_PlayerSlots.size();i<=pInstance->m_NumSlots;i++)
|
||||
{
|
||||
CPlayer *pNewPlayer=new CPlayer(i+1);
|
||||
pNewPlayer->SetUpdateCallback(pInstance->m_PlayerUpdateCB,
|
||||
pInstance->m_PlayerUpdateCBData);
|
||||
pInstance->m_Players.push_back(pNewPlayer);
|
||||
|
||||
CPlayerSlot *pNewSlot=new CPlayerSlot(i, pNewPlayer);
|
||||
pNewSlot->SetCallback(pInstance->m_PlayerSlotAssignmentCB,
|
||||
pInstance->m_PlayerSlotAssignmentCBData);
|
||||
pInstance->m_PlayerSlots.push_back(pNewSlot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CPlayer *CGameAttributes::GetPlayer(size_t id)
|
||||
{
|
||||
if (id < m_Players.size())
|
||||
return m_Players[id];
|
||||
else
|
||||
{
|
||||
LOG(CLogger::Error, L"", L"CGameAttributes::GetPlayer(): Attempt to get player %lu (while there only are %lu players)", (unsigned long)id, (unsigned long)m_Players.size());
|
||||
return m_Players[0];
|
||||
}
|
||||
}
|
||||
|
||||
CPlayerSlot *CGameAttributes::GetSlot(size_t index)
|
||||
{
|
||||
if (index < m_PlayerSlots.size())
|
||||
return m_PlayerSlots[index];
|
||||
else
|
||||
{
|
||||
LOG(CLogger::Error, L"", L"CGameAttributes::GetSlot(): Attempt to get slot %lu (while there only are %lu slots)", (unsigned long)index, (unsigned long)m_PlayerSlots.size());
|
||||
return m_PlayerSlots[0];
|
||||
}
|
||||
}
|
||||
|
||||
void CGameAttributes::FinalizeSlots()
|
||||
{
|
||||
for (size_t i=0; i<m_PlayerSlots.size(); i++) {
|
||||
CPlayerSlot *slot = m_PlayerSlots[i];
|
||||
EPlayerSlotAssignment assignment = slot->GetAssignment();
|
||||
if (assignment != SLOT_OPEN && assignment != SLOT_CLOSED)
|
||||
{
|
||||
slot->GetPlayer()->SetActive(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CGameAttributes::SetValue(const CStrW& name, const CStrW& value)
|
||||
{
|
||||
ISynchedJSProperty *prop=GetSynchedProperty(name);
|
||||
if (prop)
|
||||
{
|
||||
prop->FromString(value);
|
||||
}
|
||||
}
|
||||
|
||||
void CGameAttributes::Update(const CStrW& name, ISynchedJSProperty *attrib)
|
||||
{
|
||||
if (m_UpdateCB)
|
||||
m_UpdateCB(name, attrib->ToString(), m_UpdateCBData);
|
||||
}
|
||||
|
||||
void CGameAttributes::SetPlayerUpdateCallback(CPlayer::UpdateCallback *cb, void *userdata)
|
||||
{
|
||||
m_PlayerUpdateCB=cb;
|
||||
m_PlayerUpdateCBData=userdata;
|
||||
|
||||
for (size_t i=0;i<m_Players.size();i++)
|
||||
{
|
||||
m_Players[i]->SetUpdateCallback(cb, userdata);
|
||||
}
|
||||
}
|
||||
|
||||
void CGameAttributes::SetPlayerSlotAssignmentCallback(PlayerSlotAssignmentCB *cb, void *userdata)
|
||||
{
|
||||
m_PlayerSlotAssignmentCB=cb;
|
||||
m_PlayerSlotAssignmentCBData=userdata;
|
||||
|
||||
for (size_t i=0;i<m_PlayerSlots.size();i++)
|
||||
{
|
||||
m_PlayerSlots[i]->SetCallback(cb, userdata);
|
||||
}
|
||||
}
|
@ -1,215 +0,0 @@
|
||||
/* Copyright (C) 2009 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 0 A.D. is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef INCLUDED_GAMEATTRIBUTES
|
||||
#define INCLUDED_GAMEATTRIBUTES
|
||||
|
||||
#include "Player.h"
|
||||
|
||||
#include "scripting/SynchedJSObject.h"
|
||||
#include "simulation/LOSManager.h"
|
||||
|
||||
//class CNetServerSession;
|
||||
class CNetSession;
|
||||
class CGameAttributes;
|
||||
class CPlayerSlot;
|
||||
|
||||
enum EPlayerSlotAssignment
|
||||
{
|
||||
SLOT_CLOSED,
|
||||
SLOT_OPEN,
|
||||
SLOT_SESSION,
|
||||
SLOT_AI
|
||||
};
|
||||
|
||||
typedef void (PlayerSlotAssignmentCB)(void *data, CPlayerSlot *);
|
||||
|
||||
class CPlayerSlot: public CJSObject<CPlayerSlot>
|
||||
{
|
||||
size_t m_SlotID;
|
||||
EPlayerSlotAssignment m_Assignment;
|
||||
|
||||
//CNetServerSession *m_pSession;
|
||||
CNetSession *m_pSession;
|
||||
int m_SessionID;
|
||||
CPlayer *m_pPlayer;
|
||||
|
||||
PlayerSlotAssignmentCB *m_Callback;
|
||||
void *m_CallbackData;
|
||||
|
||||
bool JSI_AssignClosed(JSContext *cx, uintN argc, jsval *argv);
|
||||
|
||||
// Assign to a session, takes one argument (a NetSession object)
|
||||
bool JSI_AssignToSession(JSContext *cx, uintN argc, jsval *argv);
|
||||
// Assign to the local player in SP or Server Player in MP
|
||||
bool JSI_AssignLocal(JSContext *cx, uintN argc, jsval *argv);
|
||||
|
||||
bool JSI_AssignOpen(JSContext *cx, uintN argc, jsval *argv);
|
||||
|
||||
// TODO This will wait until there actually is AI to set up
|
||||
// bool JSI_AssignAI(JSContext *cx, uintN argc, jsval *argv);
|
||||
|
||||
jsval JSI_GetSession(JSContext* cx);
|
||||
jsval JSI_GetAssignment(JSContext* cx);
|
||||
|
||||
void CallCallback();
|
||||
//void SetAssignment(EPlayerSlotAssignment, CNetServerSession *pSession, int sessionID);
|
||||
void SetAssignment(EPlayerSlotAssignment, CNetSession *pSession, int sessionID);
|
||||
|
||||
protected:
|
||||
friend class CGameAttributes;
|
||||
inline void SetSlotID(size_t slotID)
|
||||
{ m_SlotID=slotID; }
|
||||
|
||||
public:
|
||||
CPlayerSlot(size_t slotID, CPlayer *pPlayer);
|
||||
~CPlayerSlot();
|
||||
|
||||
inline CPlayer *GetPlayer()
|
||||
{ return m_pPlayer; }
|
||||
inline int GetSessionID()
|
||||
{ return m_SessionID; }
|
||||
inline size_t GetSlotID()
|
||||
{ return m_SlotID; }
|
||||
|
||||
// Only applicable on the server host, and may return NULL if the slot
|
||||
// is not assigned to a server session.
|
||||
//inline CNetServerSession *GetSession()
|
||||
//{ return m_pSession; }
|
||||
inline CNetSession *GetSession()
|
||||
{ return m_pSession; }
|
||||
|
||||
|
||||
inline void SetCallback(PlayerSlotAssignmentCB *callback, void *data)
|
||||
{
|
||||
m_Callback=callback;
|
||||
m_CallbackData=data;
|
||||
}
|
||||
|
||||
inline EPlayerSlotAssignment GetAssignment()
|
||||
{ return m_Assignment; }
|
||||
|
||||
// Reset any assignment the slot might have had before and mark the slot as
|
||||
// closed.
|
||||
void AssignClosed();
|
||||
|
||||
// [Server] Assign the slot to a connected session
|
||||
//void AssignToSession(CNetServerSession *pSession);
|
||||
void AssignToSession(CNetSession *pSession);
|
||||
|
||||
// [Client] The slot has been assigned by the server to a session ID, mirror
|
||||
// the assignment
|
||||
void AssignToSessionID(int sessionID);
|
||||
|
||||
// Reset any assignment the slot might have before and mark the slot as free
|
||||
void AssignOpen();
|
||||
|
||||
// Assign to the local player in SP or Server Player in MP
|
||||
void AssignLocal();
|
||||
|
||||
// TODO This will wait until there actually is AI to set up
|
||||
// void AssignAI();
|
||||
|
||||
static void ScriptingInit();
|
||||
};
|
||||
|
||||
namespace PlayerSlotArray_JS
|
||||
{
|
||||
JSBool GetProperty( JSContext* cx, JSObject* obj, jsval id, jsval* vp );
|
||||
}
|
||||
|
||||
class CGameAttributes:
|
||||
public CSynchedJSObject<CGameAttributes>
|
||||
{
|
||||
public:
|
||||
typedef void (UpdateCallback)(const CStrW& name, const CStrW& newValue, void *data);
|
||||
|
||||
CStrW m_MapFile;
|
||||
CStrW m_ResourceLevel;
|
||||
CStrW m_StartingPhase;
|
||||
CStrW m_GameMode;
|
||||
int m_LOSSetting; // ELOSSetting
|
||||
bool m_FogOfWar;
|
||||
bool m_ScreenshotMode;
|
||||
|
||||
// Note: we must use the un-internationalized name of the resource level and starting phase
|
||||
|
||||
private:
|
||||
friend JSBool PlayerSlotArray_JS::GetProperty( JSContext* cx, JSObject* obj, jsval id, jsval* vp );
|
||||
|
||||
size_t m_NumSlots;
|
||||
|
||||
// All players in the game. m_Players[0] is the Gaia Player, like in CGame.
|
||||
// m_Players[1..n] have a corresponding player slot in m_PlayerSlots[0..n-1]
|
||||
std::vector <CPlayer *> m_Players;
|
||||
|
||||
std::vector <CPlayerSlot *> m_PlayerSlots;
|
||||
JSObject *m_PlayerSlotArrayJS;
|
||||
|
||||
UpdateCallback *m_UpdateCB;
|
||||
void *m_UpdateCBData;
|
||||
|
||||
CPlayer::UpdateCallback *m_PlayerUpdateCB;
|
||||
void *m_PlayerUpdateCBData;
|
||||
|
||||
PlayerSlotAssignmentCB *m_PlayerSlotAssignmentCB;
|
||||
void *m_PlayerSlotAssignmentCBData;
|
||||
|
||||
virtual void Update(const CStrW& name, ISynchedJSProperty *attrib);
|
||||
static void OnNumSlotsUpdate(CSynchedJSObjectBase *owner);
|
||||
|
||||
jsval JSI_GetPlayerSlots(JSContext* cx);
|
||||
jsval_t JSI_GetOpenSlot(JSContext *cx, uintN argc, jsval *argv);
|
||||
jsval JSI_GetUsedSlotsAmount(JSContext* cx);
|
||||
|
||||
public:
|
||||
CGameAttributes();
|
||||
virtual ~CGameAttributes();
|
||||
|
||||
void SetValue(const CStrW& name, const CStrW& value);
|
||||
|
||||
inline void SetUpdateCallback(UpdateCallback *cb, void *userdata)
|
||||
{
|
||||
m_UpdateCB=cb;
|
||||
m_UpdateCBData=userdata;
|
||||
}
|
||||
|
||||
inline size_t GetSlotCount()
|
||||
{ return m_NumSlots; }
|
||||
|
||||
inline CStrW GetGameMode()
|
||||
{ return m_GameMode; }
|
||||
|
||||
// Remove all slots that are either opened or closed, so that all slots have
|
||||
// an assignment and a player. Player IDs will be assigned in the same order
|
||||
// as the slot indexes, but without holes in the numbering.
|
||||
void FinalizeSlots();
|
||||
|
||||
// Get the player object for the passed Player ID
|
||||
CPlayer *GetPlayer(size_t id);
|
||||
// Get the slot object with the specified index
|
||||
CPlayerSlot *GetSlot(size_t index);
|
||||
|
||||
void SetPlayerUpdateCallback(CPlayer::UpdateCallback *cb, void *userdata);
|
||||
void SetPlayerSlotAssignmentCallback(PlayerSlotAssignmentCB *cb, void *userdata);
|
||||
|
||||
static void ScriptingInit();
|
||||
};
|
||||
|
||||
extern CGameAttributes *g_GameAttributes;
|
||||
|
||||
#endif
|
@ -37,7 +37,6 @@
|
||||
#include "ps/Filesystem.h"
|
||||
#include "ps/Font.h"
|
||||
#include "ps/Game.h"
|
||||
#include "ps/GameAttributes.h"
|
||||
#include "ps/Globals.h"
|
||||
#include "ps/Hotkey.h"
|
||||
#include "ps/Loader.h"
|
||||
@ -71,6 +70,8 @@
|
||||
#include "scripting/DOMEvent.h"
|
||||
#include "scripting/ScriptableComplex.h"
|
||||
|
||||
#include "scriptinterface/ScriptInterface.h"
|
||||
|
||||
#include "maths/scripting/JSInterface_Vector3D.h"
|
||||
|
||||
#include "graphics/scripting/JSInterface_Camera.h"
|
||||
@ -306,21 +307,10 @@ static void RegisterJavascriptInterfaces()
|
||||
JSI_Sound::ScriptingInit();
|
||||
|
||||
// scripting
|
||||
SColour::ScriptingInit();
|
||||
CScriptEvent::ScriptingInit();
|
||||
|
||||
// ps
|
||||
JSI_Console::init();
|
||||
CGameAttributes::ScriptingInit();
|
||||
CPlayerSlot::ScriptingInit();
|
||||
CPlayer::ScriptingInit();
|
||||
PlayerCollection::Init( "PlayerCollection" );
|
||||
|
||||
// network
|
||||
CNetClient::ScriptingInit();
|
||||
CNetServer::ScriptingInit();
|
||||
CNetSession::ScriptingInit();
|
||||
CServerPlayer::ScriptingInit();
|
||||
|
||||
// GUI
|
||||
CGUI::ScriptingInit();
|
||||
@ -482,12 +472,13 @@ static void InitInput()
|
||||
|
||||
in_add_handler(HotkeyInputHandler);
|
||||
|
||||
in_add_handler(GlobalsInputHandler);
|
||||
|
||||
// gui_handler needs to be registered after (i.e. called before!) the
|
||||
// hotkey handler so that input boxes can be typed in without
|
||||
// setting off hotkeys.
|
||||
in_add_handler(gui_handler);
|
||||
|
||||
// must be registered after (called before) the GUI which relies on these globals
|
||||
in_add_handler(GlobalsInputHandler);
|
||||
}
|
||||
|
||||
|
||||
@ -573,7 +564,7 @@ void EndGame()
|
||||
}
|
||||
|
||||
|
||||
void Shutdown(int flags)
|
||||
void Shutdown(int UNUSED(flags))
|
||||
{
|
||||
MICROLOG(L"Shutdown");
|
||||
|
||||
@ -582,11 +573,6 @@ void Shutdown(int flags)
|
||||
|
||||
ShutdownPs(); // Must delete g_GUI before g_ScriptingHost
|
||||
|
||||
if (! (flags & INIT_NO_SIM))
|
||||
{
|
||||
SAFE_DELETE(g_GameAttributes);
|
||||
}
|
||||
|
||||
// destroy actor related stuff
|
||||
TIMER_BEGIN(L"shutdown actor stuff");
|
||||
delete &g_MaterialManager;
|
||||
@ -803,14 +789,6 @@ void Init(const CmdLineArgs& args, int flags)
|
||||
ogl_WarnIfError();
|
||||
InitRenderer();
|
||||
|
||||
if (! (flags & INIT_NO_SIM))
|
||||
{
|
||||
g_GameAttributes = new CGameAttributes;
|
||||
|
||||
// Register a few Game/Network JS globals
|
||||
g_ScriptingHost.SetGlobal("g_GameAttributes", OBJECT_TO_JSVAL(g_GameAttributes->GetScript()));
|
||||
}
|
||||
|
||||
InitInput();
|
||||
|
||||
ogl_WarnIfError();
|
||||
@ -833,66 +811,49 @@ void RenderGui(bool RenderingState)
|
||||
class AutostartNetServer : public CNetServer
|
||||
{
|
||||
public:
|
||||
AutostartNetServer(CGame *pGame, CGameAttributes *pGameAttributes, int maxPlayers) :
|
||||
CNetServer(pGame->GetSimulation2()->GetScriptInterface(), pGame, pGameAttributes), m_NumPlayers(1), m_MaxPlayers(maxPlayers)
|
||||
AutostartNetServer(const CStr& map, int maxPlayers) :
|
||||
CNetServer(), m_NeedsStart(false), m_NumPlayers(0), m_MaxPlayers(maxPlayers)
|
||||
{
|
||||
}
|
||||
protected:
|
||||
virtual void OnPlayerJoin(CNetSession* pSession)
|
||||
{
|
||||
for (size_t slot = 0; slot < m_GameAttributes->GetSlotCount(); ++slot)
|
||||
{
|
||||
if (m_GameAttributes->GetSlot(slot)->GetAssignment() == SLOT_OPEN)
|
||||
{
|
||||
m_GameAttributes->GetSlot(slot)->AssignToSession(pSession);
|
||||
break;
|
||||
}
|
||||
CScriptValRooted attrs;
|
||||
GetScriptInterface().Eval("({})", attrs);
|
||||
GetScriptInterface().SetProperty(attrs.get(), "map", std::string(map), false);
|
||||
UpdateGameAttributes(attrs);
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void OnAddPlayer()
|
||||
{
|
||||
m_NumPlayers++;
|
||||
|
||||
debug_printf(L"# player joined (got %d, need %d)\n", (int)m_NumPlayers, (int)m_MaxPlayers);
|
||||
|
||||
if (m_NumPlayers >= m_MaxPlayers)
|
||||
{
|
||||
g_GUI->SwitchPage(L"page_loading.xml", JSVAL_VOID);
|
||||
int ret = StartGame();
|
||||
debug_assert(ret == 0);
|
||||
}
|
||||
m_NeedsStart = true; // delay until next Poll, so the new player has been fully processed
|
||||
}
|
||||
|
||||
virtual void OnPlayerLeave(CNetSession* UNUSED(pSession))
|
||||
virtual void OnRemovePlayer()
|
||||
{
|
||||
debug_warn(L"client left?!");
|
||||
m_NumPlayers--;
|
||||
}
|
||||
|
||||
virtual void Poll()
|
||||
{
|
||||
if (m_NeedsStart)
|
||||
{
|
||||
StartGame();
|
||||
m_NeedsStart = false;
|
||||
}
|
||||
|
||||
CNetServer::Poll();
|
||||
}
|
||||
|
||||
private:
|
||||
bool m_NeedsStart;
|
||||
size_t m_NumPlayers;
|
||||
size_t m_MaxPlayers;
|
||||
};
|
||||
|
||||
class AutostartNetClient : public CNetClient
|
||||
{
|
||||
public:
|
||||
AutostartNetClient(CGame *pGame, CGameAttributes *pGameAttributes) :
|
||||
CNetClient(pGame->GetSimulation2()->GetScriptInterface(), pGame, pGameAttributes)
|
||||
{
|
||||
}
|
||||
protected:
|
||||
virtual void OnConnectComplete()
|
||||
{
|
||||
debug_printf(L"# connect complete\n");
|
||||
}
|
||||
|
||||
virtual void OnStartGame()
|
||||
{
|
||||
g_GUI->SwitchPage(L"page_loading.xml", JSVAL_VOID);
|
||||
int ret = StartGame();
|
||||
debug_assert(ret == 0);
|
||||
}
|
||||
};
|
||||
|
||||
static bool Autostart(const CmdLineArgs& args)
|
||||
{
|
||||
/*
|
||||
@ -908,12 +869,6 @@ static bool Autostart(const CmdLineArgs& args)
|
||||
|
||||
g_Game = new CGame();
|
||||
|
||||
g_GameAttributes->m_MapFile = autostartMap + ".pmp";
|
||||
|
||||
// Make the whole world visible
|
||||
g_GameAttributes->m_LOSSetting = LOS_SETTING_ALL_VISIBLE;
|
||||
g_GameAttributes->m_FogOfWar = false;
|
||||
|
||||
if (args.Has("autostart-host"))
|
||||
{
|
||||
InitPs(true, L"page_loading.xml");
|
||||
@ -922,32 +877,33 @@ static bool Autostart(const CmdLineArgs& args)
|
||||
if (args.Has("autostart-players"))
|
||||
maxPlayers = args.Get("autostart-players").ToUInt();
|
||||
|
||||
g_NetServer = new AutostartNetServer(g_Game, g_GameAttributes, maxPlayers);
|
||||
// TODO: player name, etc
|
||||
bool ok = g_NetServer->Start(NULL, 0, NULL);
|
||||
g_NetServer = new AutostartNetServer(autostartMap, maxPlayers);
|
||||
bool ok = g_NetServer->SetupConnection();
|
||||
debug_assert(ok);
|
||||
|
||||
g_NetClient = new CNetClient(g_Game);
|
||||
// TODO: player name, etc
|
||||
g_NetClient->SetupLocalConnection(*g_NetServer);
|
||||
}
|
||||
else if (args.Has("autostart-client"))
|
||||
{
|
||||
InitPs(true, L"page_loading.xml");
|
||||
|
||||
bool ok;
|
||||
g_NetClient = new AutostartNetClient(g_Game, g_GameAttributes);
|
||||
g_NetClient = new CNetClient(g_Game);
|
||||
// TODO: player name, etc
|
||||
ok = g_NetClient->Create();
|
||||
debug_assert(ok);
|
||||
ok = g_NetClient->Connect(args.Get("autostart-ip"), DEFAULT_HOST_PORT);
|
||||
bool ok = g_NetClient->SetupConnection(args.Get("autostart-ip"));
|
||||
debug_assert(ok);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 1; i < 8; ++i)
|
||||
g_GameAttributes->GetSlot(i)->AssignLocal();
|
||||
CScriptValRooted attrs;
|
||||
g_Game->GetSimulation2()->GetScriptInterface().Eval("({})", attrs);
|
||||
g_Game->GetSimulation2()->GetScriptInterface().SetProperty(attrs.get(), "map", std::string(autostartMap), false);
|
||||
|
||||
PSRETURN ret = g_Game->StartGame(g_GameAttributes);
|
||||
debug_assert(ret == PSRETURN_OK);
|
||||
g_Game->SetPlayerID(1);
|
||||
g_Game->StartGame(attrs);
|
||||
LDR_NonprogressiveLoad();
|
||||
ret = g_Game->ReallyStartGame();
|
||||
PSRETURN ret = g_Game->ReallyStartGame();
|
||||
debug_assert(ret == PSRETURN_OK);
|
||||
|
||||
InitPs(true, L"page_session_new.xml");
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2009 Wildfire Games.
|
||||
/* Copyright (C) 2010 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
@ -42,11 +42,7 @@ enum InitFlags
|
||||
|
||||
// skip initializing the in-game GUI.
|
||||
// needed by map editor because it uses its own GUI.
|
||||
INIT_NO_GUI = 2,
|
||||
|
||||
// skip initializing the simulation.
|
||||
// used by actor viewer because it doesn't need the simulation code.
|
||||
INIT_NO_SIM = 4
|
||||
INIT_NO_GUI = 2
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -1,178 +0,0 @@
|
||||
/* Copyright (C) 2009 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 0 A.D. is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "precompiled.h"
|
||||
|
||||
#include "Player.h"
|
||||
#include "network/NetMessage.h"
|
||||
#include "ps/scripting/JSCollection.h"
|
||||
#include "simulation/LOSManager.h"
|
||||
|
||||
CPlayer::CPlayer(size_t playerID):
|
||||
m_PlayerID(playerID),
|
||||
m_Name(CStrW(L"Player #")+CStrW((unsigned)playerID)),
|
||||
m_Civilization(L""),
|
||||
m_Colour(0.7f, 0.7f, 0.7f),
|
||||
m_UpdateCB(0),
|
||||
m_Active(false)
|
||||
{
|
||||
// m_LOSToken = LOS_GetTokenFor(playerID);
|
||||
|
||||
// Initialize diplomacy: we need to be neutral to Gaia and enemy to everyone else;
|
||||
// however, if we are Gaia, we'll just be neutral to everyone; finally, everyone
|
||||
// will be allied with themselves.
|
||||
m_DiplomaticStance[0] = DIPLOMACY_NEUTRAL;
|
||||
for(int i=1; i<=PS_MAX_PLAYERS; i++)
|
||||
{
|
||||
m_DiplomaticStance[i] = (m_PlayerID==0 ? DIPLOMACY_NEUTRAL : DIPLOMACY_ENEMY);
|
||||
}
|
||||
m_DiplomaticStance[m_PlayerID] = DIPLOMACY_ALLIED;
|
||||
|
||||
AddSynchedProperty( L"name", &m_Name );
|
||||
AddSynchedProperty( L"civilization", &m_Civilization );
|
||||
|
||||
// HACK - since we have to use setColour to update this, we don't want to
|
||||
// expose a colour property. Meanwhile, we want to have a property "colour"
|
||||
// available to be able to use the update/sync system.
|
||||
// So, this is only added to the SynchedProperties list and not also passed
|
||||
// to CJSObject's list
|
||||
ISynchedJSProperty *prop=new CSynchedJSProperty<SPlayerColour>(L"colour", &m_Colour, this);
|
||||
m_SynchedProperties[L"colour"]=prop;
|
||||
|
||||
// HACK - maintain diplomacy synced in the same way, by adding a dummy property for each stance
|
||||
for(int i=0; i<=PS_MAX_PLAYERS; i++)
|
||||
{
|
||||
CStrW name = CStrW(L"diplomaticStance_") + CStrW(i);
|
||||
ISynchedJSProperty *prop=new CSynchedJSProperty<int>(name, &m_DiplomaticStance[i], this);
|
||||
m_SynchedProperties[name]=prop;
|
||||
}
|
||||
}
|
||||
|
||||
CPlayer::~CPlayer()
|
||||
{
|
||||
// Side-effect of HACKs - since these properties are not passed to CJSObject's list,
|
||||
// they don't get freed automatically
|
||||
|
||||
delete m_SynchedProperties[L"colour"];
|
||||
|
||||
for(int i=0; i<=PS_MAX_PLAYERS; i++)
|
||||
{
|
||||
CStrW name = CStrW(L"diplomaticStance_") + CStrW(i);
|
||||
delete m_SynchedProperties[name];
|
||||
}
|
||||
}
|
||||
|
||||
void CPlayer::ScriptingInit()
|
||||
{
|
||||
g_ScriptingHost.DefineConstant("DIPLOMACY_ENEMY", DIPLOMACY_ENEMY);
|
||||
g_ScriptingHost.DefineConstant("DIPLOMACY_NEUTRAL", DIPLOMACY_NEUTRAL);
|
||||
g_ScriptingHost.DefineConstant("DIPLOMACY_ALLIED", DIPLOMACY_ALLIED);
|
||||
|
||||
AddMethod<CStrW, &CPlayer::JSI_ToString>("toString", 0);
|
||||
AddMethod<void, &CPlayer::JSI_SetColour>("setColour", 1);
|
||||
AddMethod<jsval_t, &CPlayer::JSI_GetColour>("getColour", 0);
|
||||
AddMethod<void, &CPlayer::JSI_SetDiplomaticStance>("setDiplomaticStance", 2);
|
||||
AddMethod<jsval_t, &CPlayer::JSI_GetDiplomaticStance>("getDiplomaticStance", 1);
|
||||
|
||||
AddProperty( L"id", &CPlayer::m_PlayerID, true );
|
||||
AddProperty( L"active", &CPlayer::m_Active, true );
|
||||
AddProperty( L"name", &CPlayer::m_Name, true );
|
||||
// MT: Work out how this fits with the Synched stuff...
|
||||
|
||||
// AddClassProperty( L"name", &CPlayer::m_Name );
|
||||
// AddClassProperty( L"colour", &CPlayer::m_Colour );
|
||||
|
||||
CJSObject<CPlayer>::ScriptingInit( "Player" );
|
||||
}
|
||||
|
||||
void CPlayer::Update(const CStrW& name, ISynchedJSProperty *prop)
|
||||
{
|
||||
if (m_UpdateCB)
|
||||
m_UpdateCB(name, prop->ToString(), this, m_UpdateCBData);
|
||||
}
|
||||
|
||||
void CPlayer::SetValue(const CStrW& name, const CStrW& value)
|
||||
{
|
||||
ISynchedJSProperty *prop=GetSynchedProperty(name);
|
||||
if (prop)
|
||||
{
|
||||
prop->FromString(value);
|
||||
}
|
||||
}
|
||||
|
||||
bool CPlayer::ValidateCommand(CNetMessage* UNUSED(pMsg))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
CStrW CPlayer::JSI_ToString( JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv) )
|
||||
{
|
||||
return L"[object Player: " + m_Name + L"]";
|
||||
}
|
||||
|
||||
void CPlayer::JSI_SetColour( JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* argv )
|
||||
{
|
||||
m_Colour=*( ToNative<SPlayerColour>(argv[0]) );
|
||||
ISynchedJSProperty *prop=GetSynchedProperty(L"colour");
|
||||
Update(L"colour", prop);
|
||||
}
|
||||
|
||||
jsval_t CPlayer::JSI_GetColour( JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv) )
|
||||
{
|
||||
//ISynchedJSProperty *prop=GetSynchedProperty(L"colour");
|
||||
//return prop->Get(cx, this);
|
||||
SPlayerColour* col = &m_Colour;
|
||||
return ToJSVal(col);
|
||||
}
|
||||
|
||||
void CPlayer::JSI_SetDiplomaticStance(JSContext *cx, uintN UNUSED(argc), jsval *argv)
|
||||
{
|
||||
try
|
||||
{
|
||||
CPlayer* player = ToPrimitive<CPlayer*>( argv[0] );
|
||||
int stance = ToPrimitive<int>( argv[1] );
|
||||
if (! (stance==DIPLOMACY_ENEMY || stance==DIPLOMACY_NEUTRAL || stance==DIPLOMACY_ALLIED))
|
||||
{
|
||||
JS_ReportError(cx, "Argument 2 must be a valid stance ID");
|
||||
return;
|
||||
}
|
||||
|
||||
m_DiplomaticStance[player->m_PlayerID] = (EDiplomaticStance) stance;
|
||||
|
||||
CStrW name = CStrW(L"diplomaticStance_") + CStrW((unsigned)player->m_PlayerID);
|
||||
ISynchedJSProperty *prop=GetSynchedProperty(name);
|
||||
Update(name, prop);
|
||||
}
|
||||
catch( PSERROR_Scripting_ConversionFailed )
|
||||
{
|
||||
JS_ReportError( cx, "Could not convert argument 1 to a Player object" );
|
||||
}
|
||||
}
|
||||
|
||||
jsval_t CPlayer::JSI_GetDiplomaticStance(JSContext *cx, uintN UNUSED(argc), jsval *argv)
|
||||
{
|
||||
try
|
||||
{
|
||||
CPlayer* player = ToPrimitive<CPlayer*>( argv[0] );
|
||||
return ToJSVal( (int) m_DiplomaticStance[player->m_PlayerID] );
|
||||
}
|
||||
catch( PSERROR_Scripting_ConversionFailed )
|
||||
{
|
||||
JS_ReportError( cx, "Could not convert argument 1 to a Player object" );
|
||||
return JSVAL_VOID;
|
||||
}
|
||||
}
|
@ -1,126 +0,0 @@
|
||||
/* Copyright (C) 2009 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 0 A.D. is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef INCLUDED_PLAYER
|
||||
#define INCLUDED_PLAYER
|
||||
|
||||
#include "CStr.h"
|
||||
#include "scripting/SynchedJSObject.h"
|
||||
#include "scripting/ScriptableObject.h"
|
||||
#include "scripting/ScriptCustomTypes.h"
|
||||
#include "ps/scripting/JSCollection.h"
|
||||
#include "Game.h"
|
||||
|
||||
class CNetMessage;
|
||||
class HEntity;
|
||||
class CTechnology;
|
||||
|
||||
typedef SColour SPlayerColour;
|
||||
|
||||
enum EDiplomaticStance
|
||||
{
|
||||
DIPLOMACY_ENEMY,
|
||||
DIPLOMACY_NEUTRAL,
|
||||
DIPLOMACY_ALLIED
|
||||
};
|
||||
|
||||
const size_t invalidPlayerId = ~size_t(0); // rationale: see Unit.h
|
||||
|
||||
class CPlayer : public CSynchedJSObject<CPlayer>
|
||||
{
|
||||
public:
|
||||
typedef void (UpdateCallback)(const CStrW& name, const CStrW& value, CPlayer *player, void *userdata);
|
||||
|
||||
private:
|
||||
CStrW m_Name;
|
||||
CStrW m_Civilization; // Note: this must be the un-internationalized name of the civ
|
||||
size_t m_PlayerID;
|
||||
size_t m_LOSToken;
|
||||
SPlayerColour m_Colour;
|
||||
int /*EDiplomaticStance*/ m_DiplomaticStance[PS_MAX_PLAYERS+1];
|
||||
std::vector<CTechnology*> m_ActiveTechs;
|
||||
bool m_Active; // Is this an active player, or a dummy?
|
||||
|
||||
UpdateCallback *m_UpdateCB;
|
||||
void *m_UpdateCBData;
|
||||
|
||||
virtual void Update(const CStrW& name, ISynchedJSProperty *prop);
|
||||
|
||||
public:
|
||||
CPlayer( size_t playerID );
|
||||
~CPlayer();
|
||||
|
||||
bool ValidateCommand(CNetMessage *pMsg);
|
||||
|
||||
inline size_t GetPlayerID() const
|
||||
{ return m_PlayerID; }
|
||||
inline void SetPlayerID(size_t id)
|
||||
{ m_PlayerID=id; }
|
||||
|
||||
inline size_t GetLOSToken() const
|
||||
{ return m_LOSToken; }
|
||||
|
||||
inline const CStrW& GetName() const
|
||||
{ return m_Name; }
|
||||
inline void SetName(const CStrW& name)
|
||||
{ m_Name = name; }
|
||||
|
||||
inline const SPlayerColour &GetColour() const
|
||||
{ return m_Colour; }
|
||||
inline void SetColour(const SPlayerColour &colour)
|
||||
{ m_Colour = colour; }
|
||||
|
||||
inline EDiplomaticStance GetDiplomaticStance(CPlayer* other) const
|
||||
{ return (EDiplomaticStance)m_DiplomaticStance[other->m_PlayerID]; }
|
||||
inline void SetDiplomaticStance(CPlayer* other, EDiplomaticStance stance)
|
||||
{ m_DiplomaticStance[other->m_PlayerID] = stance; }
|
||||
|
||||
inline void SetUpdateCallback(UpdateCallback *cb, void *userdata)
|
||||
{
|
||||
m_UpdateCB=cb;
|
||||
m_UpdateCBData=userdata;
|
||||
}
|
||||
void SetValue(const CStrW& name, const CStrW& value);
|
||||
|
||||
inline void AddActiveTech(CTechnology* tech)
|
||||
{
|
||||
m_ActiveTechs.push_back(tech);
|
||||
}
|
||||
|
||||
inline const std::vector<CTechnology*>& GetActiveTechs()
|
||||
{
|
||||
return m_ActiveTechs;
|
||||
}
|
||||
|
||||
inline bool IsActive() const
|
||||
{ return m_Active; }
|
||||
inline void SetActive(bool active)
|
||||
{ m_Active = active; }
|
||||
|
||||
// JS Interface Functions
|
||||
CStrW JSI_ToString( JSContext* context, uintN argc, jsval* argv );
|
||||
void JSI_SetColour(JSContext *context, uintN argc, jsval *argv);
|
||||
jsval_t JSI_GetColour(JSContext *context, uintN argc, jsval *argv);
|
||||
void JSI_SetDiplomaticStance(JSContext *context, uintN argc, jsval *argv);
|
||||
jsval_t JSI_GetDiplomaticStance(JSContext *context, uintN argc, jsval *argv);
|
||||
|
||||
static void ScriptingInit();
|
||||
};
|
||||
|
||||
typedef CJSCollection<CPlayer*, &CPlayer::JSI_class> PlayerCollection;
|
||||
|
||||
#endif
|
@ -35,7 +35,6 @@
|
||||
#include "ps/CStr.h"
|
||||
#include "ps/Errors.h"
|
||||
#include "ps/Game.h"
|
||||
#include "ps/GameAttributes.h"
|
||||
#include "ps/Loader.h"
|
||||
#include "ps/LoaderThunks.h"
|
||||
#include "ps/World.h"
|
||||
@ -68,25 +67,30 @@ CWorld::CWorld(CGame *pGame):
|
||||
|
||||
/**
|
||||
* Sets up the game world and loads required world resources.
|
||||
*
|
||||
* @param CGameAttributes * pGameAttributes pointer to the game attribute values.
|
||||
**/
|
||||
void CWorld::Initialize(CGameAttributes *pAttribs)
|
||||
void CWorld::Initialize(const CStrW& mapFile)
|
||||
{
|
||||
// Load the map, if one was specified
|
||||
if (pAttribs->m_MapFile.length())
|
||||
if (mapFile.length())
|
||||
{
|
||||
VfsPath mapfilename(VfsPath(L"maps/scenarios/")/(std::wstring)pAttribs->m_MapFile);
|
||||
VfsPath mapfilename(VfsPath(L"maps/scenarios/")/(std::wstring)mapFile);
|
||||
CMapReader* reader = 0;
|
||||
|
||||
try {
|
||||
try
|
||||
{
|
||||
reader = new CMapReader;
|
||||
CTriggerManager* pTriggerManager = NULL;
|
||||
reader->LoadMap(mapfilename, m_Terrain, m_UnitManager, g_Renderer.GetWaterManager(),
|
||||
g_Renderer.GetSkyManager(), &g_LightEnv, m_pGame->GetView()->GetCamera(), m_pGame->GetView()->GetCinema(),
|
||||
reader->LoadMap(mapfilename, m_Terrain, m_UnitManager,
|
||||
CRenderer::IsInitialised() ? g_Renderer.GetWaterManager() : NULL,
|
||||
CRenderer::IsInitialised() ? g_Renderer.GetSkyManager() : NULL,
|
||||
&g_LightEnv,
|
||||
m_pGame->GetView() ? m_pGame->GetView()->GetCamera() : NULL,
|
||||
m_pGame->GetView() ? m_pGame->GetView()->GetCinema() : NULL,
|
||||
pTriggerManager, m_pGame->GetSimulation2(), NULL);
|
||||
// fails immediately, or registers for delay loading
|
||||
} catch (PSERROR_File& err) {
|
||||
}
|
||||
catch (PSERROR_File& err)
|
||||
{
|
||||
delete reader;
|
||||
LOG(CLogger::Error, LOG_CATEGORY, L"Failed to load map %ls: %hs", mapfilename.string().c_str(), err.what());
|
||||
throw PSERROR_Game_World_MapLoadFailed();
|
||||
@ -97,12 +101,10 @@ void CWorld::Initialize(CGameAttributes *pAttribs)
|
||||
|
||||
/**
|
||||
* Initializes the game world with the attributes provided.
|
||||
*
|
||||
* @param CGameAttributes * pGameAttributes pointer to the game attribute values.
|
||||
**/
|
||||
void CWorld::RegisterInit(CGameAttributes *pAttribs)
|
||||
void CWorld::RegisterInit(const CStrW& mapFile)
|
||||
{
|
||||
Initialize(pAttribs);
|
||||
Initialize(mapFile);
|
||||
}
|
||||
|
||||
|
||||
|
@ -34,13 +34,13 @@ ERROR_SUBGROUP(Game, World);
|
||||
ERROR_TYPE(Game_World, MapLoadFailed);
|
||||
|
||||
class CGame;
|
||||
class CGameAttributes;
|
||||
class CUnitManager;
|
||||
class CEntityManager;
|
||||
class CProjectileManager;
|
||||
class CLOSManager;
|
||||
class CTerritoryManager;
|
||||
class CTerrain;
|
||||
class CStrW;
|
||||
|
||||
/**
|
||||
* CWorld is a general data class containing whatever is needed to accurately represent the world.
|
||||
@ -76,11 +76,11 @@ public:
|
||||
CWorld(CGame *pGame);
|
||||
~CWorld();
|
||||
|
||||
void RegisterInit(CGameAttributes *pGameAttributes);
|
||||
void RegisterInit(const CStrW& mapFile);
|
||||
/*
|
||||
Initialize the World - load the map and all objects
|
||||
*/
|
||||
void Initialize(CGameAttributes *pGameAttributes);
|
||||
void Initialize(const CStrW& mapFile);
|
||||
|
||||
// provided for JS _rewritemaps function
|
||||
void RewriteMap();
|
||||
|
@ -1,19 +0,0 @@
|
||||
/* Copyright (C) 2009 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 0 A.D. is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "precompiled.h"
|
||||
#include "JSCollection.h"
|
@ -1,448 +0,0 @@
|
||||
/* Copyright (C) 2009 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 0 A.D. is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// JSCollection.h
|
||||
//
|
||||
// A Collection object for JavaScript to hold one specific type of
|
||||
// object.
|
||||
|
||||
#include "scripting/ScriptingHost.h"
|
||||
#include "scripting/ScriptObject.h"
|
||||
#include "scripting/JSConversions.h"
|
||||
|
||||
#ifndef INCLUDED_JSCOLLECTION
|
||||
#define INCLUDED_JSCOLLECTION
|
||||
|
||||
template<typename T, JSClass* ScriptType> class CJSCollection
|
||||
{
|
||||
public:
|
||||
class CJSCollectionData
|
||||
{
|
||||
public:
|
||||
std::vector<T>* m_Data;
|
||||
bool m_EngineOwned;
|
||||
CJSCollectionData() { m_Data = new std::vector<T>; m_EngineOwned = false; }
|
||||
CJSCollectionData( const std::vector<T>& Copy ) { m_Data = new std::vector<T>( Copy ); m_EngineOwned = false; }
|
||||
CJSCollectionData( std::vector<T>* Reference ) { m_Data = Reference; m_EngineOwned = true; }
|
||||
~CJSCollectionData() { if( !m_EngineOwned ) delete( m_Data ); }
|
||||
};
|
||||
static JSClass JSI_class;
|
||||
private:
|
||||
|
||||
static JSPropertySpec JSI_props[];
|
||||
static JSFunctionSpec JSI_methods[];
|
||||
|
||||
static JSBool ToString( JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval );
|
||||
static JSBool Subset( JSContext* cx, JSObject* obj, uintN argc, jsval* agv, jsval* rval );
|
||||
static JSBool Push( JSContext* cx, JSObject* obj, uintN argc, jsval* agv, jsval* rval );
|
||||
static JSBool Pop( JSContext* cx, JSObject* obj, uintN argc, jsval* agv, jsval* rval );
|
||||
static JSBool Remove( JSContext* cx, JSObject* obj, uintN argc, jsval* agv, jsval* rval );
|
||||
static JSBool GetLength( JSContext* cx, JSObject* obj, jsval id, jsval* vp );
|
||||
static JSBool IsEmpty( JSContext* cx, JSObject* obj, jsval id, jsval* vp );
|
||||
static JSBool Clear( JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval );
|
||||
static JSBool Equals( JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval );
|
||||
static JSBool AddProperty( JSContext* cx, JSObject* obj, jsval id, jsval* vp );
|
||||
static JSBool RemoveProperty( JSContext* cx, JSObject* obj, jsval id, jsval* vp );
|
||||
static JSBool GetProperty( JSContext* cx, JSObject* obj, jsval id, jsval* vp );
|
||||
static JSBool SetProperty( JSContext* cx, JSObject* obj, jsval id, jsval* vp );
|
||||
static void Finalize( JSContext* cx, JSObject* obj );
|
||||
static bool GetNative( jsval m, T& Storage );
|
||||
|
||||
public:
|
||||
static void Init( const char* ClassName );
|
||||
static JSObject* Create( const std::vector<T>& Copy );
|
||||
static JSObject* CreateReference( std::vector<T>* Reference );
|
||||
static std::vector<T>* RetrieveSet( JSContext* cx, JSObject* obj );
|
||||
};
|
||||
|
||||
template<typename T, JSClass* ScriptType> JSClass CJSCollection<T, ScriptType>::JSI_class =
|
||||
{
|
||||
0, JSCLASS_HAS_PRIVATE,
|
||||
AddProperty, RemoveProperty,
|
||||
GetProperty, SetProperty,
|
||||
JS_EnumerateStub, JS_ResolveStub,
|
||||
JS_ConvertStub, Finalize,
|
||||
NULL, NULL, NULL, NULL,
|
||||
NULL, NULL, NULL, NULL
|
||||
};
|
||||
|
||||
template<typename T, JSClass* ScriptType> JSPropertySpec CJSCollection<T, ScriptType>::JSI_props[] =
|
||||
{
|
||||
{ "length", 0, JSPROP_PERMANENT | JSPROP_READONLY, GetLength, NULL },
|
||||
{ "empty", 0, JSPROP_PERMANENT | JSPROP_READONLY, IsEmpty, NULL },
|
||||
{ NULL, 0, 0, NULL, NULL }
|
||||
};
|
||||
|
||||
template<typename T, JSClass* ScriptType> JSFunctionSpec CJSCollection<T, ScriptType>::JSI_methods[] =
|
||||
{
|
||||
{ "toString", ToString, 0, 0, 0 },
|
||||
{ "subset", Subset, 1, 0, 0 },
|
||||
{ "push", Push, 1, 0, 0 },
|
||||
{ "pop", Pop, 0, 0, 0 },
|
||||
{ "remove", Remove, 1, 0, 0 },
|
||||
{ "clear", Clear, 0, 0, 0 },
|
||||
{ "equals", Equals, 1, 0, 0 },
|
||||
{ NULL, NULL, 0, 0, 0 },
|
||||
};
|
||||
|
||||
template<typename T, JSClass* ScriptType> std::vector<T>* CJSCollection<T, ScriptType>::RetrieveSet( JSContext* cx, JSObject* obj )
|
||||
{
|
||||
CJSCollectionData* Info = (CJSCollectionData*)JS_GetInstancePrivate( cx, obj, &JSI_class, NULL );
|
||||
if( !Info ) return( NULL );
|
||||
return( Info->m_Data );
|
||||
}
|
||||
|
||||
template<typename T, JSClass* ScriptType> JSBool CJSCollection<T, ScriptType>::AddProperty ( JSContext* cx, JSObject* obj, jsval id, jsval* vp )
|
||||
{
|
||||
if( !JSVAL_IS_INT( id ) )
|
||||
return( JS_TRUE ); // Accessing a named property; nothing interesting.
|
||||
|
||||
int index = JSVAL_TO_INT( id );
|
||||
|
||||
std::vector<T>* set = RetrieveSet( cx, obj );
|
||||
|
||||
if( !set )
|
||||
return( JS_TRUE ); // That's odd; we've lost the pointer.
|
||||
|
||||
if( ( index >= 0 ) && ( index < (int)set->size() ) )
|
||||
return( JS_TRUE );
|
||||
|
||||
if( index != (int)set->size() )
|
||||
{
|
||||
// If you add something to the collection, it must be at the
|
||||
// next empty array element; i.e. set->size()
|
||||
JS_ReportError( cx, "Cannot create a property at that subscript" );
|
||||
return( JS_FALSE );
|
||||
}
|
||||
|
||||
T m;
|
||||
|
||||
if( !GetNative( *vp, m ) )
|
||||
return( JS_TRUE );
|
||||
|
||||
set->resize( index + 1 );
|
||||
set->at( index ) = m;
|
||||
|
||||
return( JS_TRUE );
|
||||
}
|
||||
|
||||
|
||||
template<typename T, JSClass* ScriptType> JSBool CJSCollection<T, ScriptType>::RemoveProperty (
|
||||
JSContext* cx, JSObject* obj, jsval id, jsval* UNUSED(vp) )
|
||||
{
|
||||
if( !JSVAL_IS_INT( id ) )
|
||||
return( JS_TRUE ); // Accessing a named property; nothing interesting.
|
||||
|
||||
int index = JSVAL_TO_INT( id );
|
||||
|
||||
std::vector<T>* set = RetrieveSet( cx, obj );
|
||||
|
||||
if( !set )
|
||||
return( JS_TRUE ); // That's odd; we've lost the pointer.
|
||||
|
||||
if( ( index < 0 ) || ( index >= (int)set->size() ) )
|
||||
{
|
||||
JS_ReportError( cx, "Index out of bounds." );
|
||||
return( JS_TRUE );
|
||||
}
|
||||
|
||||
set->erase( set->begin() + index );
|
||||
|
||||
return( JS_TRUE );
|
||||
}
|
||||
|
||||
template<typename T, JSClass* ScriptType> JSBool CJSCollection<T, ScriptType>::GetProperty ( JSContext* cx, JSObject* obj, jsval id, jsval* vp )
|
||||
{
|
||||
if( !JSVAL_IS_INT( id ) )
|
||||
return( JS_TRUE ); // Accessing a named property; nothing interesting.
|
||||
|
||||
int index = JSVAL_TO_INT( id );
|
||||
std::vector<T>* set = RetrieveSet( cx, obj );
|
||||
|
||||
if( !set )
|
||||
return( JS_FALSE ); // That's odd; we've lost the pointer.
|
||||
|
||||
if( ( index < 0 ) || ( index >= (int)set->size() ) )
|
||||
{
|
||||
JS_ReportError( cx, "Index out of bounds." );
|
||||
return( JS_TRUE );
|
||||
}
|
||||
|
||||
*vp = ToJSVal( set->at( index ) );
|
||||
|
||||
return( JS_TRUE );
|
||||
}
|
||||
|
||||
template<typename T, JSClass* ScriptType> JSBool CJSCollection<T, ScriptType>::SetProperty ( JSContext* cx, JSObject* obj, jsval id, jsval* vp )
|
||||
{
|
||||
if( !JSVAL_IS_INT( id ) )
|
||||
return( JS_TRUE ); // Accessing a named property; nothing interesting.
|
||||
|
||||
int index = JSVAL_TO_INT( id );
|
||||
|
||||
std::vector<T>* set = RetrieveSet( cx, obj );
|
||||
|
||||
if( !set )
|
||||
return( JS_FALSE ); // That's odd; we've lost the pointer.
|
||||
|
||||
if( ( index < 0 ) || ( index >= (int)set->size() ) )
|
||||
{
|
||||
JS_ReportError( cx, "Index out of bounds." );
|
||||
return( JS_TRUE );
|
||||
}
|
||||
|
||||
T m;
|
||||
|
||||
if( !GetNative( *vp, m ) )
|
||||
return( JS_TRUE );
|
||||
|
||||
set->at( index ) = m;
|
||||
|
||||
return( JS_TRUE );
|
||||
}
|
||||
|
||||
template<typename T, JSClass* ScriptType> void CJSCollection<T, ScriptType>::Finalize ( JSContext* cx, JSObject* obj )
|
||||
{
|
||||
CJSCollectionData* info = (CJSCollectionData*)JS_GetPrivate( cx, obj );
|
||||
if( info )
|
||||
delete( info );
|
||||
}
|
||||
|
||||
template<typename T, JSClass* ScriptType> JSObject* CJSCollection<T, ScriptType>::Create( const std::vector<T>& Copy )
|
||||
{
|
||||
JSObject* Collection = JS_NewObject( g_ScriptingHost.GetContext(), &JSI_class, NULL, NULL );
|
||||
JS_SetPrivate( g_ScriptingHost.GetContext(), Collection, new CJSCollectionData( Copy ) );
|
||||
|
||||
return( Collection );
|
||||
}
|
||||
|
||||
template<typename T, JSClass* ScriptType> JSObject* CJSCollection<T, ScriptType>::CreateReference( std::vector<T>* Reference )
|
||||
{
|
||||
JSObject* Collection = JS_NewObject( g_ScriptingHost.GetContext(), &JSI_class, NULL, NULL );
|
||||
JS_SetPrivate( g_ScriptingHost.GetContext(), Collection, new CJSCollectionData( Reference ) );
|
||||
|
||||
return( Collection );
|
||||
}
|
||||
|
||||
template<typename T, JSClass* ScriptType> void CJSCollection<T, ScriptType>::Init( const char* ClassName )
|
||||
{
|
||||
JSI_class.name = ClassName;
|
||||
g_ScriptingHost.DefineCustomObjectType( &JSI_class, NULL, 0, JSI_props, JSI_methods, NULL, NULL );
|
||||
}
|
||||
|
||||
template<typename T, JSClass* ScriptType> bool CJSCollection<T, ScriptType>::GetNative( jsval m, T& Storage )
|
||||
{
|
||||
if( ToPrimitive( g_ScriptingHost.GetContext(), m, Storage ) )
|
||||
return( true );
|
||||
|
||||
JS_ReportError( g_ScriptingHost.GetContext(), "Only objects of type %s can be stored in this collection.", ScriptType->name );
|
||||
return( false );
|
||||
}
|
||||
|
||||
|
||||
template<typename T, JSClass* ScriptType> JSBool CJSCollection<T, ScriptType>::ToString(
|
||||
JSContext* cx, JSObject* obj, uintN UNUSED(argc), jsval* UNUSED(argv), jsval* rval )
|
||||
{
|
||||
std::vector<T>* set = RetrieveSet( cx, obj );
|
||||
|
||||
if( !set )
|
||||
return( JS_FALSE ); // That's odd; we've lost the pointer.
|
||||
|
||||
wchar_t buffer[256];
|
||||
int len = swprintf_s( buffer, ARRAY_SIZE(buffer), L"[object Collection: %hs: %ld members]", ScriptType->name, set->size() );
|
||||
buffer[255] = 0;
|
||||
if (len < 0 || len > 255) len=255;
|
||||
utf16string u16str(buffer, buffer+len);
|
||||
*rval = STRING_TO_JSVAL( JS_NewUCStringCopyZ( cx, u16str.c_str() ) );
|
||||
return( JS_TRUE );
|
||||
}
|
||||
|
||||
template<typename T, JSClass* ScriptType> JSBool CJSCollection<T, ScriptType>::GetLength(
|
||||
JSContext* cx, JSObject* obj, jsval UNUSED(id), jsval* vp )
|
||||
{
|
||||
std::vector<T>* set = RetrieveSet( cx, obj );
|
||||
|
||||
if( !set )
|
||||
return( JS_FALSE ); // That's odd; we've lost the pointer.
|
||||
|
||||
*vp = INT_TO_JSVAL( set->size() );
|
||||
return( JS_TRUE );
|
||||
}
|
||||
|
||||
template<typename T, JSClass* ScriptType> JSBool CJSCollection<T, ScriptType>::IsEmpty(
|
||||
JSContext* cx, JSObject* obj, jsval UNUSED(id), jsval* vp )
|
||||
{
|
||||
std::vector<T>* set = RetrieveSet( cx, obj );
|
||||
|
||||
if( !set )
|
||||
return( JS_FALSE ); // That's odd; we've lost the pointer.
|
||||
|
||||
*vp = BOOLEAN_TO_JSVAL( set->empty() );
|
||||
return( JS_TRUE );
|
||||
}
|
||||
|
||||
template<typename T, JSClass* ScriptType> JSBool CJSCollection<T, ScriptType>::Equals( JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval )
|
||||
{
|
||||
std::vector<T>* a = RetrieveSet( cx, obj );
|
||||
if( !a )
|
||||
return( JS_FALSE );
|
||||
if( ( argc == 0 ) || ( !JSVAL_IS_OBJECT( argv[0] ) ) ) return( JS_FALSE );
|
||||
std::vector<T>* b = RetrieveSet( cx, JSVAL_TO_OBJECT( argv[0] ) );
|
||||
if( !b )
|
||||
return( JS_FALSE );
|
||||
typename std::vector<T>::iterator ita, itb;
|
||||
|
||||
size_t seek = a->size();
|
||||
for( ita = a->begin(); ita != a->end(); ita++ )
|
||||
for( itb = b->begin(); itb != b->end(); itb++ )
|
||||
if( *ita == *itb ) { seek--; break; }
|
||||
|
||||
if( seek )
|
||||
{
|
||||
*rval = JSVAL_FALSE;
|
||||
return( JS_TRUE );
|
||||
}
|
||||
|
||||
seek = b->size();
|
||||
for( itb = b->begin(); itb != b->end(); itb++ )
|
||||
for( ita = a->begin(); ita != a->end(); ita++ )
|
||||
if( *ita == *itb ) { seek--; break; }
|
||||
|
||||
if( seek )
|
||||
{
|
||||
*rval = JSVAL_FALSE;
|
||||
return( JS_TRUE );
|
||||
}
|
||||
|
||||
*rval = JSVAL_TRUE;
|
||||
return( JS_TRUE );
|
||||
}
|
||||
|
||||
template<typename T, JSClass* ScriptType> JSBool CJSCollection<T, ScriptType>::Subset(
|
||||
JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval )
|
||||
{
|
||||
debug_assert( argc > 0 );
|
||||
|
||||
std::vector<T>* Set = RetrieveSet( cx, obj );
|
||||
if( !Set )
|
||||
return( JS_FALSE );
|
||||
|
||||
CJSCollectionData* CollectionData = new CJSCollectionData();
|
||||
CollectionData->m_EngineOwned = false;
|
||||
|
||||
typename std::vector<T>::iterator it;
|
||||
|
||||
CScriptObject Predicate( argv[0] );
|
||||
|
||||
JSObject* Collection = JS_NewObject( g_ScriptingHost.GetContext(), &JSI_class, NULL, NULL );
|
||||
JS_SetPrivate( g_ScriptingHost.GetContext(), Collection, CollectionData );
|
||||
|
||||
for( it = Set->begin(); it != Set->end(); it++ )
|
||||
if( Predicate.Run( ToScript( (T*)&( *it ) ) ) )
|
||||
CollectionData->m_Data->push_back( *it );
|
||||
|
||||
*rval = OBJECT_TO_JSVAL( Collection );
|
||||
|
||||
return( JS_TRUE );
|
||||
}
|
||||
|
||||
template<typename T, JSClass* ScriptType> JSBool CJSCollection<T, ScriptType>::Clear(
|
||||
JSContext* cx, JSObject* obj, uintN UNUSED(argc), jsval* UNUSED(argv), jsval* rval )
|
||||
{
|
||||
std::vector<T>* Set = RetrieveSet( cx, obj );
|
||||
if( !Set )
|
||||
return( JS_FALSE );
|
||||
|
||||
Set->clear();
|
||||
|
||||
*rval = JSVAL_TRUE;
|
||||
|
||||
return( JS_TRUE );
|
||||
}
|
||||
|
||||
template<typename T, JSClass* ScriptType> JSBool CJSCollection<T, ScriptType>::Push( JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval )
|
||||
{
|
||||
debug_assert( argc > 0 );
|
||||
|
||||
std::vector<T>* Set = RetrieveSet( cx, obj );
|
||||
if( !Set )
|
||||
return( JS_FALSE );
|
||||
|
||||
for( int i = 0; i < (int)argc; i++ )
|
||||
{
|
||||
T m;
|
||||
if( GetNative( argv[i], m ) )
|
||||
Set->push_back( m );
|
||||
}
|
||||
|
||||
*rval = INT_TO_JSVAL( Set->size() );
|
||||
return( JS_TRUE );
|
||||
}
|
||||
|
||||
template<typename T, JSClass* ScriptType> JSBool CJSCollection<T, ScriptType>::Pop(
|
||||
JSContext* cx, JSObject* obj, uintN UNUSED(argc), jsval* UNUSED(argv), jsval* rval )
|
||||
{
|
||||
std::vector<T>* Set = RetrieveSet( cx, obj );
|
||||
if( !Set )
|
||||
return( JS_FALSE );
|
||||
|
||||
if( !Set->size() )
|
||||
{
|
||||
JS_ReportError( cx, "Can't pop members from an empty Collection." );
|
||||
return( JS_TRUE );
|
||||
}
|
||||
|
||||
*rval = ToJSVal( Set->back() );
|
||||
Set->pop_back();
|
||||
|
||||
return( JS_TRUE );
|
||||
}
|
||||
|
||||
template<typename T, JSClass* ScriptType> JSBool CJSCollection<T, ScriptType>::Remove(
|
||||
JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* UNUSED(rval) )
|
||||
{
|
||||
debug_assert( argc > 0 );
|
||||
|
||||
|
||||
std::vector<T>* Set = RetrieveSet( cx, obj );
|
||||
|
||||
if( !Set )
|
||||
return( JS_TRUE ); // That's odd; we've lost the pointer.
|
||||
|
||||
int index;
|
||||
try
|
||||
{
|
||||
index = ToPrimitive<int>( argv[0] );
|
||||
}
|
||||
catch( PSERROR_Scripting_ConversionFailed )
|
||||
{
|
||||
JS_ReportError( cx, "Index must be an integer." );
|
||||
return( JS_TRUE );
|
||||
}
|
||||
|
||||
if( ( index < 0 ) || ( index >= (int)Set->size() ) )
|
||||
{
|
||||
JS_ReportError( cx, "Index out of bounds." );
|
||||
return( JS_TRUE );
|
||||
}
|
||||
|
||||
Set->erase( Set->begin() + index );
|
||||
|
||||
return( JS_TRUE );
|
||||
}
|
||||
|
||||
#endif
|
@ -1,115 +0,0 @@
|
||||
/* Copyright (C) 2009 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 0 A.D. is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef INCLUDED_JSMAP
|
||||
#define INCLUDED_JSMAP
|
||||
|
||||
/*
|
||||
A simple read-only JS wrapper for map types (STL associative containers).
|
||||
Has been tested with integer keys. Writeability shouldn't be all that hard
|
||||
to add, it's just not needed by the current users of the class.
|
||||
*/
|
||||
template <typename T, typename KeyType = typename T::key_type>
|
||||
class CJSMap
|
||||
{
|
||||
T *m_pInstance;
|
||||
JSObject *m_JSObject;
|
||||
|
||||
typedef typename T::iterator iterator;
|
||||
|
||||
static JSBool GetProperty( JSContext* cx, JSObject* obj, jsval id, jsval* vp )
|
||||
{
|
||||
CJSMap<T, KeyType> *object=ToNative<CJSMap<T, KeyType> >(cx, obj);
|
||||
T *pInstance = object->m_pInstance;
|
||||
KeyType key=ToPrimitive<KeyType>(id);
|
||||
|
||||
iterator it;
|
||||
it=pInstance->find(key);
|
||||
if (it == pInstance->end())
|
||||
return JS_FALSE;
|
||||
|
||||
*vp=OBJECT_TO_JSVAL(ToScript(it->second));
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
static JSBool SetProperty( JSContext* UNUSED(cx), JSObject* UNUSED(obj), jsval UNUSED(id), jsval* UNUSED(vp) )
|
||||
{
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
void CreateScriptObject()
|
||||
{
|
||||
if( !m_JSObject )
|
||||
{
|
||||
m_JSObject = JS_NewObject( g_ScriptingHost.GetContext(), &JSI_class, NULL, NULL );
|
||||
JS_AddRoot( g_ScriptingHost.GetContext(), (void*)&m_JSObject );
|
||||
JS_SetPrivate( g_ScriptingHost.GetContext(), m_JSObject, this );
|
||||
}
|
||||
}
|
||||
|
||||
void ReleaseScriptObject()
|
||||
{
|
||||
if( m_JSObject )
|
||||
{
|
||||
JS_SetPrivate( g_ScriptingHost.GetContext(), m_JSObject, NULL );
|
||||
JS_RemoveRoot( g_ScriptingHost.GetContext(), &m_JSObject );
|
||||
m_JSObject = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
CJSMap(T* pInstance):
|
||||
m_pInstance(pInstance),
|
||||
m_JSObject(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
~CJSMap()
|
||||
{
|
||||
ReleaseScriptObject();
|
||||
}
|
||||
|
||||
JSObject *GetScript()
|
||||
{
|
||||
if (!m_JSObject)
|
||||
CreateScriptObject();
|
||||
return m_JSObject;
|
||||
}
|
||||
|
||||
static void ScriptingInit(const char *className)
|
||||
{
|
||||
JSI_class.name=className;
|
||||
g_ScriptingHost.DefineCustomObjectType(&JSI_class,
|
||||
NULL, 0, NULL, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
// Needs to be public for ToScript/ToNative
|
||||
static JSClass JSI_class;
|
||||
};
|
||||
|
||||
template<typename T, typename KeyType>
|
||||
JSClass CJSMap<T, KeyType>::JSI_class =
|
||||
{
|
||||
NULL, JSCLASS_HAS_PRIVATE,
|
||||
JS_PropertyStub, JS_PropertyStub,
|
||||
GetProperty, SetProperty,
|
||||
JS_EnumerateStub, JS_ResolveStub,
|
||||
JS_ConvertStub, JS_FinalizeStub,
|
||||
NULL, NULL, NULL, NULL
|
||||
};
|
||||
|
||||
#endif // INCLUDED_JSMAP
|
@ -35,9 +35,7 @@
|
||||
#include "ps/Game.h"
|
||||
#include "ps/Profile.h"
|
||||
#include "ps/Filesystem.h"
|
||||
#include "ps/Game.h"
|
||||
#include "ps/World.h"
|
||||
#include "ps/Player.h"
|
||||
#include "ps/Loader.h"
|
||||
#include "ps/ProfileViewer.h"
|
||||
#include "graphics/Camera.h"
|
||||
|
@ -21,27 +21,11 @@
|
||||
#include "graphics/ObjectManager.h"
|
||||
#include "maths/scripting/JSInterface_Vector3D.h"
|
||||
#include "ps/Parser.h"
|
||||
#include "ps/Player.h"
|
||||
#include "lib/sysdep/sysdep.h" // isfinite
|
||||
#include <math.h>
|
||||
#include <cfloat>
|
||||
#include "scripting/ScriptableComplex.inl"
|
||||
|
||||
// CPlayer*
|
||||
template<> bool ToPrimitive<CPlayer*>( JSContext* cx, jsval v, CPlayer*& Storage )
|
||||
{
|
||||
if( !JSVAL_IS_OBJECT( v ) || ( v == JSVAL_NULL ) ) return( false );
|
||||
CPlayer* Data = (CPlayer*)JS_GetInstancePrivate( cx, JSVAL_TO_OBJECT( v ), &CPlayer::JSI_class, NULL );
|
||||
if( !Data ) return( false );
|
||||
Storage = Data;
|
||||
return( true );
|
||||
}
|
||||
|
||||
template<> JSObject* ToScript<CPlayer*>( CPlayer** Native )
|
||||
{
|
||||
return( ToScript<CPlayer>( *Native ) );
|
||||
}
|
||||
|
||||
// CVector3D
|
||||
|
||||
template<> CVector3D* ToNative<CVector3D>( JSContext* cx, JSObject* obj )
|
||||
|
@ -26,7 +26,6 @@ class CStrW;
|
||||
class CScriptObject;
|
||||
class CObjectEntry;
|
||||
class CVector3D;
|
||||
class CPlayer;
|
||||
|
||||
// -----
|
||||
//
|
||||
@ -112,10 +111,6 @@ template<> jsval ToJSVal<CVector3D>( const CVector3D& Native );
|
||||
template<> bool ToPrimitive<CObjectEntry>( JSContext* cx, jsval v, CObjectEntry*& Storage );
|
||||
template<> jsval ToJSVal<CObjectEntry>( CObjectEntry*& Native );
|
||||
|
||||
// CPlayer*
|
||||
template<> bool ToPrimitive<CPlayer*>( JSContext* cx, jsval v, CPlayer*& Storage );
|
||||
template<> JSObject* ToScript<CPlayer*>( CPlayer** Native );
|
||||
|
||||
// CScriptObject
|
||||
template<> bool ToPrimitive<CScriptObject>( JSContext* cx, jsval v, CScriptObject& Storage );
|
||||
template<> jsval ToJSVal<CScriptObject>( CScriptObject& Native );
|
||||
|
@ -39,25 +39,23 @@
|
||||
#include "lib/svn_revision.h"
|
||||
#include "lib/frequency_filter.h"
|
||||
#include "maths/scripting/JSInterface_Vector3D.h"
|
||||
#include "network/NetClient.h"
|
||||
#include "network/NetServer.h"
|
||||
#include "ps/CConsole.h"
|
||||
#include "ps/CLogger.h"
|
||||
#include "ps/CStr.h"
|
||||
#include "ps/Game.h"
|
||||
#include "ps/GameAttributes.h"
|
||||
#include "ps/Globals.h" // g_frequencyFilter
|
||||
#include "ps/GameSetup/GameSetup.h"
|
||||
#include "ps/Hotkey.h"
|
||||
#include "ps/ProfileViewer.h"
|
||||
#include "ps/World.h"
|
||||
#include "ps/i18n.h"
|
||||
#include "ps/scripting/JSCollection.h"
|
||||
#include "ps/scripting/JSInterface_Console.h"
|
||||
#include "ps/scripting/JSInterface_VFS.h"
|
||||
#include "renderer/Renderer.h"
|
||||
#include "renderer/SkyManager.h"
|
||||
#include "scriptinterface/ScriptInterface.h"
|
||||
#include "simulation/LOSManager.h"
|
||||
#include "simulation2/Simulation2.h"
|
||||
|
||||
#define LOG_CATEGORY L"script"
|
||||
@ -194,90 +192,6 @@ JSBool StopJsTimer(JSContext* cx, JSObject*, uintN argc, jsval* argv, jsval* rva
|
||||
// Game Setup
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// Create a new network server object.
|
||||
// params:
|
||||
// returns: net server object
|
||||
JSBool CreateServer(JSContext* cx, JSObject*, uintN argc, jsval* argv, jsval* rval)
|
||||
{
|
||||
JSU_REQUIRE_NO_PARAMS();
|
||||
|
||||
if( !g_Game )
|
||||
g_Game = new CGame();
|
||||
if( !g_NetServer )
|
||||
g_NetServer = new CNetServer(g_Game->GetSimulation2()->GetScriptInterface(), g_Game, g_GameAttributes);
|
||||
|
||||
*rval = OBJECT_TO_JSVAL(g_NetServer->GetScript());
|
||||
return( JS_TRUE );
|
||||
}
|
||||
|
||||
|
||||
// Create a new network client object.
|
||||
// params:
|
||||
// returns: net client object
|
||||
JSBool CreateClient(JSContext* cx, JSObject*, uintN argc, jsval* argv, jsval* rval)
|
||||
{
|
||||
JSU_REQUIRE_NO_PARAMS();
|
||||
|
||||
if( !g_Game )
|
||||
g_Game = new CGame();
|
||||
if( !g_NetClient )
|
||||
g_NetClient = new CNetClient(g_Game->GetSimulation2()->GetScriptInterface(), g_Game, g_GameAttributes);
|
||||
|
||||
*rval = OBJECT_TO_JSVAL(g_NetClient->GetScript());
|
||||
return( JS_TRUE );
|
||||
}
|
||||
|
||||
|
||||
// Begin the process of starting a game.
|
||||
// params:
|
||||
// returns: success [bool]
|
||||
// notes:
|
||||
// - Performs necessary initialization while calling back into the
|
||||
// main loop, so the game remains responsive to display+user input.
|
||||
// - When complete, the engine calls the reallyStartGame JS function.
|
||||
// TODO: Replace StartGame with Create(Game|Server|Client)/game.start() -
|
||||
// after merging CGame and CGameAttributes
|
||||
JSBool StartGame(JSContext* cx, JSObject*, uintN argc, jsval* argv, jsval* rval)
|
||||
{
|
||||
JSU_REQUIRE_NO_PARAMS();
|
||||
|
||||
*rval = BOOLEAN_TO_JSVAL(JS_TRUE);
|
||||
|
||||
// Hosted MP Game
|
||||
if (g_NetServer)
|
||||
{
|
||||
*rval = BOOLEAN_TO_JSVAL(g_NetServer->StartGame() == 0);
|
||||
}
|
||||
// Joined MP Game
|
||||
else if (g_NetClient)
|
||||
{
|
||||
*rval = BOOLEAN_TO_JSVAL(g_NetClient->StartGame() == 0);
|
||||
}
|
||||
// Start an SP Game Session
|
||||
else if (!g_Game)
|
||||
{
|
||||
g_Game = new CGame();
|
||||
PSRETURN ret = g_Game->StartGame(g_GameAttributes);
|
||||
if (ret != PSRETURN_OK)
|
||||
{
|
||||
// Failed to start the game - destroy it, and return false
|
||||
|
||||
delete g_Game;
|
||||
g_Game = NULL;
|
||||
|
||||
*rval = BOOLEAN_TO_JSVAL(JS_FALSE);
|
||||
return( JS_TRUE );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
*rval = BOOLEAN_TO_JSVAL(JS_FALSE);
|
||||
}
|
||||
|
||||
return( JS_TRUE );
|
||||
}
|
||||
|
||||
|
||||
// Immediately ends the current game (if any).
|
||||
// params:
|
||||
// returns:
|
||||
@ -289,14 +203,6 @@ JSBool EndGame(JSContext* cx, JSObject*, uintN argc, jsval* argv, jsval* rval)
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
JSBool GetGameMode(JSContext* cx, JSObject*, uintN argc, jsval* argv, jsval* rval)
|
||||
{
|
||||
JSU_REQUIRE_NO_PARAMS();
|
||||
|
||||
*rval = ToJSVal( g_GameAttributes->GetGameMode() );
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Internationalization
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -391,7 +297,10 @@ JSBool ForceGarbageCollection(JSContext* cx, JSObject* UNUSED(obj), uintN argc,
|
||||
JSBool GetFps( JSContext* cx, JSObject*, uintN argc, jsval* argv, jsval* rval )
|
||||
{
|
||||
JSU_REQUIRE_NO_PARAMS();
|
||||
*rval = INT_TO_JSVAL(g_frequencyFilter->StableFrequency());
|
||||
int freq = 0;
|
||||
if (g_frequencyFilter)
|
||||
freq = g_frequencyFilter->StableFrequency();
|
||||
*rval = INT_TO_JSVAL(freq);
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
@ -703,11 +612,7 @@ JSFunctionSpec ScriptFunctionTable[] =
|
||||
JS_FUNC("stopXTimer", StopJsTimer, 1)
|
||||
|
||||
// Game Setup
|
||||
JS_FUNC("startGame", StartGame, 0)
|
||||
JS_FUNC("endGame", EndGame, 0)
|
||||
JS_FUNC("getGameMode", GetGameMode, 0)
|
||||
JS_FUNC("createClient", CreateClient, 0)
|
||||
JS_FUNC("createServer", CreateServer, 0)
|
||||
|
||||
// VFS (external)
|
||||
JS_FUNC("buildDirEntList", JSI_VFS::BuildDirEntList, 1)
|
||||
@ -757,45 +662,6 @@ JSFunctionSpec ScriptFunctionTable[] =
|
||||
// property accessors
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
JSBool GetPlayerSet( JSContext* UNUSED(cx), JSObject* UNUSED(obj), jsval UNUSED(id), jsval* vp )
|
||||
{
|
||||
std::vector<CPlayer*>* players = g_Game->GetPlayers();
|
||||
|
||||
*vp = OBJECT_TO_JSVAL( PlayerCollection::Create( *players ) );
|
||||
|
||||
return( JS_TRUE );
|
||||
}
|
||||
|
||||
|
||||
JSBool GetLocalPlayer( JSContext* UNUSED(cx), JSObject* UNUSED(obj), jsval UNUSED(id), jsval* vp )
|
||||
{
|
||||
*vp = OBJECT_TO_JSVAL( g_Game->GetLocalPlayer()->GetScript() );
|
||||
return( JS_TRUE );
|
||||
}
|
||||
|
||||
|
||||
JSBool GetGaiaPlayer( JSContext* UNUSED(cx), JSObject* UNUSED(obj), jsval UNUSED(id), jsval* vp )
|
||||
{
|
||||
*vp = OBJECT_TO_JSVAL( g_Game->GetPlayer( 0 )->GetScript() );
|
||||
return( JS_TRUE );
|
||||
}
|
||||
|
||||
|
||||
JSBool SetLocalPlayer( JSContext* cx, JSObject* UNUSED(obj), jsval UNUSED(id), jsval* vp )
|
||||
{
|
||||
CPlayer* newLocalPlayer = ToNative<CPlayer>( *vp );
|
||||
|
||||
if( !newLocalPlayer )
|
||||
{
|
||||
JS_ReportError( cx, "Not a valid Player." );
|
||||
return( JS_TRUE );
|
||||
}
|
||||
|
||||
g_Game->SetLocalPlayer( newLocalPlayer );
|
||||
return( JS_TRUE );
|
||||
}
|
||||
|
||||
|
||||
JSBool GetGameView( JSContext* UNUSED(cx), JSObject* UNUSED(obj), jsval UNUSED(id), jsval* vp )
|
||||
{
|
||||
if (g_Game)
|
||||
@ -830,9 +696,6 @@ JSPropertySpec ScriptGlobalTable[] =
|
||||
{ "camera" , GLOBAL_CAMERA, JSPROP_PERMANENT, JSI_Camera::getCamera, JSI_Camera::setCamera },
|
||||
{ "console" , GLOBAL_CONSOLE, JSPROP_PERMANENT|JSPROP_READONLY, JSI_Console::getConsole, 0 },
|
||||
{ "lightenv" , GLOBAL_LIGHTENV, JSPROP_PERMANENT, JSI_LightEnv::getLightEnv, JSI_LightEnv::setLightEnv },
|
||||
{ "players" , 0, JSPROP_PERMANENT|JSPROP_READONLY, GetPlayerSet, 0 },
|
||||
{ "localPlayer", 0, JSPROP_PERMANENT, GetLocalPlayer, SetLocalPlayer },
|
||||
{ "gaiaPlayer" , 0, JSPROP_PERMANENT|JSPROP_READONLY, GetGaiaPlayer, 0 },
|
||||
{ "gameView" , 0, JSPROP_PERMANENT|JSPROP_READONLY, GetGameView, 0 },
|
||||
{ "renderer" , 0, JSPROP_PERMANENT|JSPROP_READONLY, GetRenderer, 0 },
|
||||
|
||||
|
@ -48,3 +48,9 @@ jsval CScriptValRooted::get() const
|
||||
return JSVAL_VOID;
|
||||
return *m_Val;
|
||||
}
|
||||
|
||||
bool CScriptValRooted::undefined() const
|
||||
{
|
||||
return (!m_Val || *m_Val == JSVAL_VOID);
|
||||
|
||||
}
|
||||
|
@ -47,6 +47,8 @@ public:
|
||||
|
||||
jsval get() const;
|
||||
|
||||
bool undefined() const;
|
||||
|
||||
private:
|
||||
boost::shared_ptr<jsval> m_Val;
|
||||
};
|
||||
|
@ -37,6 +37,16 @@ public:
|
||||
m_Script.CallVoid("SetName", name);
|
||||
}
|
||||
|
||||
virtual void SetCiv(const std::wstring& civcode)
|
||||
{
|
||||
m_Script.CallVoid("SetCiv", civcode);
|
||||
}
|
||||
|
||||
virtual void SetColour(u8 r, u8 g, u8 b)
|
||||
{
|
||||
m_Script.CallVoid("SetColour", (u32)r, (u32)g, (u32)b);
|
||||
}
|
||||
|
||||
virtual CColor GetColour()
|
||||
{
|
||||
return m_Script.Call<CColor>("GetColour");
|
||||
|
@ -31,7 +31,8 @@ class ICmpPlayer : public IComponent
|
||||
{
|
||||
public:
|
||||
virtual void SetName(const std::wstring& name) = 0;
|
||||
// TODO: some more data
|
||||
virtual void SetCiv(const std::wstring& civcode) = 0;
|
||||
virtual void SetColour(u8 r, u8 g, u8 b) = 0;
|
||||
|
||||
virtual CColor GetColour() = 0;
|
||||
|
||||
|
@ -38,7 +38,6 @@
|
||||
namespace AtlasMessage {
|
||||
|
||||
static bool g_IsInitialised = false;
|
||||
static bool g_DidInitSim;
|
||||
|
||||
MESSAGEHANDLER(Init)
|
||||
{
|
||||
@ -69,12 +68,10 @@ MESSAGEHANDLER(Init)
|
||||
g_Quickstart = true;
|
||||
|
||||
int flags = INIT_HAVE_VMODE|INIT_NO_GUI;
|
||||
if (! msg->initsimulation)
|
||||
flags |= INIT_NO_SIM;
|
||||
|
||||
Init(g_GameLoop->args, flags);
|
||||
|
||||
g_DidInitSim = msg->initsimulation; // so we can shut down the right things later
|
||||
// TODO: we don't use msg->initsimulation any more, it should be deleted
|
||||
|
||||
#if OS_WIN
|
||||
// HACK (to stop things looking very ugly when scrolling) - should
|
||||
@ -103,8 +100,6 @@ MESSAGEHANDLER(Shutdown)
|
||||
g_GameLoop->view = View::GetView_None();
|
||||
|
||||
int flags = 0;
|
||||
if (! g_DidInitSim)
|
||||
flags |= INIT_NO_SIM;
|
||||
Shutdown(flags);
|
||||
|
||||
g_IsInitialised = false;
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2009 Wildfire Games.
|
||||
/* Copyright (C) 2010 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
@ -27,10 +27,10 @@
|
||||
#include "graphics/TextureEntry.h"
|
||||
#include "graphics/TextureManager.h"
|
||||
#include "ps/Game.h"
|
||||
#include "ps/GameAttributes.h"
|
||||
#include "ps/Loader.h"
|
||||
#include "ps/World.h"
|
||||
#include "renderer/Renderer.h"
|
||||
#include "scriptinterface/ScriptInterface.h"
|
||||
#include "simulation/LOSManager.h"
|
||||
#include "simulation/Simulation.h"
|
||||
#include "simulation2/Simulation2.h"
|
||||
@ -40,7 +40,7 @@
|
||||
|
||||
namespace
|
||||
{
|
||||
void InitGame(const CStrW& map)
|
||||
void InitGame()
|
||||
{
|
||||
if (g_Game)
|
||||
{
|
||||
@ -48,48 +48,23 @@ namespace
|
||||
g_Game = NULL;
|
||||
}
|
||||
|
||||
// Set attributes for the game:
|
||||
|
||||
g_GameAttributes->m_MapFile = map;
|
||||
// Make all players locally controlled
|
||||
for (int i = 1; i < 8; ++i)
|
||||
g_GameAttributes->GetSlot(i)->AssignLocal();
|
||||
|
||||
// Make the whole world visible
|
||||
g_GameAttributes->m_LOSSetting = LOS_SETTING_ALL_VISIBLE;
|
||||
g_GameAttributes->m_FogOfWar = false;
|
||||
|
||||
// Don't use screenshot mode, because we want working AI for the
|
||||
// simulation-testing. Outside that simulation-testing, we avoid having
|
||||
// the units move into attack mode by never calling CEntity::update.
|
||||
g_GameAttributes->m_ScreenshotMode = false;
|
||||
|
||||
// Initialise the game:
|
||||
g_Game = new CGame();
|
||||
|
||||
// Default to player 1 for playtesting
|
||||
g_Game->SetPlayerID(1);
|
||||
}
|
||||
|
||||
void AddDefaultPlayers()
|
||||
void StartGame(const CStrW& map)
|
||||
{
|
||||
CmpPtr<ICmpPlayerManager> cmpPlayerMan(*g_Game->GetSimulation2(), SYSTEM_ENTITY);
|
||||
debug_assert(!cmpPlayerMan.null());
|
||||
CStrW mapBase = map.BeforeLast(L".pmp"); // strip the file extension, if any
|
||||
|
||||
// TODO: pick a sensible number, give them names and colours etc
|
||||
size_t numPlayers = 4;
|
||||
for (size_t i = 0; i < numPlayers; ++i)
|
||||
{
|
||||
entity_id_t ent = g_Game->GetSimulation2()->AddEntity(L"special/player");
|
||||
cmpPlayerMan->AddPlayer(ent);
|
||||
}
|
||||
// Also TODO: Maybe it'd be sensible to load this from a map XML file via CMapReader,
|
||||
// rather than duplicating the creation code here?
|
||||
}
|
||||
CScriptValRooted attrs;
|
||||
g_Game->GetSimulation2()->GetScriptInterface().Eval("({})", attrs);
|
||||
g_Game->GetSimulation2()->GetScriptInterface().SetProperty(attrs.get(), "map", std::wstring(mapBase), false);
|
||||
|
||||
void StartGame()
|
||||
{
|
||||
PSRETURN ret = g_Game->StartGame(g_GameAttributes);
|
||||
debug_assert(ret == PSRETURN_OK);
|
||||
g_Game->StartGame(attrs);
|
||||
LDR_NonprogressiveLoad();
|
||||
ret = g_Game->ReallyStartGame();
|
||||
PSRETURN ret = g_Game->ReallyStartGame();
|
||||
debug_assert(ret == PSRETURN_OK);
|
||||
}
|
||||
}
|
||||
@ -98,58 +73,20 @@ namespace AtlasMessage {
|
||||
|
||||
MESSAGEHANDLER(GenerateMap)
|
||||
{
|
||||
InitGame(L"");
|
||||
InitGame();
|
||||
|
||||
// Convert size in patches to number of vertices
|
||||
int vertices = msg->size * PATCH_SIZE + 1;
|
||||
|
||||
// Generate flat heightmap
|
||||
u16* heightmap = new u16[vertices*vertices];
|
||||
for (int z = 0; z < vertices; ++z)
|
||||
for (int x = 0; x < vertices; ++x)
|
||||
heightmap[x + z*vertices] = 16384;
|
||||
|
||||
// Initialise terrain using the heightmap
|
||||
CTerrain* terrain = g_Game->GetWorld()->GetTerrain();
|
||||
terrain->Initialize(msg->size, heightmap);
|
||||
|
||||
delete[] heightmap;
|
||||
|
||||
AddDefaultPlayers();
|
||||
|
||||
// Start the game, load data files - this must be done before initialising
|
||||
// the terrain texture below, since the terrains must be loaded before being
|
||||
// used.
|
||||
StartGame();
|
||||
|
||||
// Cover terrain with default texture
|
||||
// TODO: split into fCoverWithTexture
|
||||
CTextureEntry* texentry = g_TexMan.FindTexture("grass1_spring"); // TODO: make default customisable
|
||||
|
||||
int patchesPerSide = terrain->GetPatchesPerSide();
|
||||
for (int pz = 0; pz < patchesPerSide; ++pz)
|
||||
{
|
||||
for (int px = 0; px < patchesPerSide; ++px)
|
||||
{
|
||||
CPatch* patch = terrain->GetPatch(px, pz); // can't fail
|
||||
|
||||
for (ssize_t z = 0; z < PATCH_SIZE; ++z)
|
||||
{
|
||||
for (ssize_t x = 0; x < PATCH_SIZE; ++x)
|
||||
{
|
||||
patch->m_MiniPatches[z][x].Tex = texentry;
|
||||
patch->m_MiniPatches[z][x].Priority = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Load the empty default map
|
||||
StartGame(L"_default");
|
||||
|
||||
// TODO: use msg->size somehow
|
||||
// (e.g. load the map then resize the terrain to match it)
|
||||
UNUSED2(msg);
|
||||
}
|
||||
|
||||
MESSAGEHANDLER(LoadMap)
|
||||
{
|
||||
InitGame(*msg->filename);
|
||||
StartGame();
|
||||
InitGame();
|
||||
StartGame(*msg->filename);
|
||||
}
|
||||
|
||||
MESSAGEHANDLER(SaveMap)
|
||||
|
Loading…
Reference in New Issue
Block a user