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="mustard">191 191 2</color>
|
||||||
<color name="brown">159 98 24</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
|
||||||
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
|
||||||
|
@ -47,7 +47,7 @@
|
|||||||
</sprite>
|
</sprite>
|
||||||
|
|
||||||
<sprite name="bkBorderBlack">
|
<sprite name="bkBorderBlack">
|
||||||
<image backcolor="black"
|
<image backcolor="black"
|
||||||
size="-1 -1 100%+1 0"
|
size="-1 -1 100%+1 0"
|
||||||
/>
|
/>
|
||||||
<image backcolor="black"
|
<image backcolor="black"
|
||||||
@ -62,21 +62,19 @@
|
|||||||
</sprite>
|
</sprite>
|
||||||
|
|
||||||
<sprite name="bkFillBlack">
|
<sprite name="bkFillBlack">
|
||||||
<image backcolor="black"
|
<image backcolor="black"/>
|
||||||
size="0 0 100% 100%"
|
|
||||||
/>
|
|
||||||
</sprite>
|
</sprite>
|
||||||
|
|
||||||
<sprite name="bkFillWhite">
|
<sprite name="bkFillWhite">
|
||||||
<image backcolor="white"
|
<image backcolor="white"/>
|
||||||
size="0 0 100% 100%"
|
|
||||||
/>
|
|
||||||
</sprite>
|
</sprite>
|
||||||
|
|
||||||
<sprite name="bkFillGray">
|
<sprite name="bkFillGray">
|
||||||
<image backcolor="gray"
|
<image backcolor="gray"/>
|
||||||
size="0 0 100% 100%"
|
</sprite>
|
||||||
/>
|
|
||||||
|
<sprite name="bkFillDarkBlue">
|
||||||
|
<image backcolor="63 127 210 255"/>
|
||||||
</sprite>
|
</sprite>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
@ -145,13 +143,14 @@
|
|||||||
/>
|
/>
|
||||||
</sprite>
|
</sprite>
|
||||||
|
|
||||||
<sprite name="bkGraniteBorderBlack">
|
<sprite name="bkGraniteBorderBlack">
|
||||||
<image texture="global/tile/granite.dds"
|
<image texture="global/tile/granite.dds"/>
|
||||||
size="0 0 100% 100%"
|
<image
|
||||||
|
backcolor="transparent"
|
||||||
border="true"
|
border="true"
|
||||||
bordercolor="black"
|
bordercolor="black"
|
||||||
/>
|
/>
|
||||||
</sprite>
|
</sprite>
|
||||||
|
|
||||||
<sprite name="textureSand">
|
<sprite name="textureSand">
|
||||||
<image texture="global/tile/sandstone.dds"
|
<image texture="global/tile/sandstone.dds"
|
||||||
@ -481,6 +480,24 @@
|
|||||||
/>
|
/>
|
||||||
</sprite>
|
</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
|
SCREENSHOT WATERMARK
|
||||||
|
@ -135,6 +135,13 @@
|
|||||||
textcolor="black"
|
textcolor="black"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<style name="wheatInput"
|
||||||
|
sprite="wheatIndentFillVeryLight"
|
||||||
|
sprite_selectarea="bkFillDarkBlue"
|
||||||
|
textcolor="black"
|
||||||
|
textcolor_selected="white"
|
||||||
|
/>
|
||||||
|
|
||||||
<style name="wheatArrowLeft"
|
<style name="wheatArrowLeft"
|
||||||
font="serif-14"
|
font="serif-14"
|
||||||
sprite="wheatCheckBoxOpen"
|
sprite="wheatCheckBoxOpen"
|
||||||
@ -207,8 +214,8 @@
|
|||||||
sprite2_pressed="wheatArrowDnOver"
|
sprite2_pressed="wheatArrowDnOver"
|
||||||
|
|
||||||
buffer_zone="10"
|
buffer_zone="10"
|
||||||
dropdown_size="100"
|
dropdown_size="200"
|
||||||
sprite_list="textureGranite"
|
sprite_list="bkGraniteBorderBlack"
|
||||||
sprite_selectarea="bkGrayBorderBlack"
|
sprite_selectarea="bkGrayBorderBlack"
|
||||||
textcolor_selected="white"
|
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,203 +1,203 @@
|
|||||||
<!--
|
<!--
|
||||||
Types
|
Types
|
||||||
-->
|
-->
|
||||||
<!ENTITY % bool "(true|false)">
|
<!ENTITY % bool "(true|false)">
|
||||||
<!ENTITY % align "(left|center|right)">
|
<!ENTITY % align "(left|center|right)">
|
||||||
<!ENTITY % valign "(top|center|bottom)">
|
<!ENTITY % valign "(top|center|bottom)">
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Defines
|
Defines
|
||||||
-->
|
-->
|
||||||
<!ENTITY % unique_settings
|
<!ENTITY % unique_settings
|
||||||
"name CDATA #IMPLIED
|
"name CDATA #IMPLIED
|
||||||
type CDATA 'empty'
|
type CDATA 'empty'
|
||||||
style CDATA #IMPLIED"
|
style CDATA #IMPLIED"
|
||||||
>
|
>
|
||||||
|
|
||||||
<!ENTITY % base_settings
|
<!ENTITY % base_settings
|
||||||
"absolute %bool; #IMPLIED
|
"absolute %bool; #IMPLIED
|
||||||
enabled %bool; #IMPLIED
|
enabled %bool; #IMPLIED
|
||||||
ghost %bool; #IMPLIED
|
ghost %bool; #IMPLIED
|
||||||
hidden %bool; #IMPLIED
|
hidden %bool; #IMPLIED
|
||||||
size CDATA #IMPLIED
|
size CDATA #IMPLIED
|
||||||
z CDATA #IMPLIED"
|
z CDATA #IMPLIED"
|
||||||
>
|
>
|
||||||
|
|
||||||
|
|
||||||
<!-- Defaults are not put in here, because it ruins the concept of styles -->
|
<!-- Defaults are not put in here, because it ruins the concept of styles -->
|
||||||
|
|
||||||
<!ENTITY % ex_settings
|
<!ENTITY % ex_settings
|
||||||
"buffer_zone CDATA #IMPLIED
|
"buffer_zone CDATA #IMPLIED
|
||||||
button_width CDATA #IMPLIED
|
button_width CDATA #IMPLIED
|
||||||
checked %bool; #IMPLIED
|
checked %bool; #IMPLIED
|
||||||
dropdown_size CDATA #IMPLIED
|
dropdown_size CDATA #IMPLIED
|
||||||
dropdown_buffer CDATA #IMPLIED
|
dropdown_buffer CDATA #IMPLIED
|
||||||
font CDATA #IMPLIED
|
font CDATA #IMPLIED
|
||||||
fov_wedge_color CDATA #IMPLIED
|
fov_wedge_color CDATA #IMPLIED
|
||||||
hotkey CDATA #IMPLIED
|
hotkey CDATA #IMPLIED
|
||||||
cell_id CDATA #IMPLIED
|
cell_id CDATA #IMPLIED
|
||||||
input_initvalue_destroyed_at_focus %bool; #IMPLIED
|
input_initvalue_destroyed_at_focus %bool; #IMPLIED
|
||||||
max_length CDATA #IMPLIED
|
max_length CDATA #IMPLIED
|
||||||
multiline %bool; #IMPLIED
|
multiline %bool; #IMPLIED
|
||||||
rectcolor_selected CDATA #IMPLIED
|
rectcolor_selected CDATA #IMPLIED
|
||||||
scrollbar %bool; #IMPLIED
|
scrollbar %bool; #IMPLIED
|
||||||
scrollbar_style CDATA #IMPLIED
|
scrollbar_style CDATA #IMPLIED
|
||||||
sprite CDATA #IMPLIED
|
sprite CDATA #IMPLIED
|
||||||
sprite2 CDATA #IMPLIED
|
sprite2 CDATA #IMPLIED
|
||||||
sprite_bar CDATA #IMPLIED
|
sprite_bar CDATA #IMPLIED
|
||||||
sprite_background CDATA #IMPLIED
|
sprite_background CDATA #IMPLIED
|
||||||
sprite_disabled CDATA #IMPLIED
|
sprite_disabled CDATA #IMPLIED
|
||||||
sprite_list CDATA #IMPLIED
|
sprite_list CDATA #IMPLIED
|
||||||
sprite2_disabled CDATA #IMPLIED
|
sprite2_disabled CDATA #IMPLIED
|
||||||
sprite_over CDATA #IMPLIED
|
sprite_over CDATA #IMPLIED
|
||||||
sprite2_over CDATA #IMPLIED
|
sprite2_over CDATA #IMPLIED
|
||||||
sprite_pressed CDATA #IMPLIED
|
sprite_pressed CDATA #IMPLIED
|
||||||
sprite2_pressed CDATA #IMPLIED
|
sprite2_pressed CDATA #IMPLIED
|
||||||
sprite_selectarea CDATA #IMPLIED
|
sprite_selectarea CDATA #IMPLIED
|
||||||
square_side CDATA #IMPLIED
|
square_side CDATA #IMPLIED
|
||||||
textcolor CDATA #IMPLIED
|
textcolor CDATA #IMPLIED
|
||||||
textcolor_disabled CDATA #IMPLIED
|
textcolor_disabled CDATA #IMPLIED
|
||||||
textcolor_over CDATA #IMPLIED
|
textcolor_over CDATA #IMPLIED
|
||||||
textcolor_pressed CDATA #IMPLIED
|
textcolor_pressed CDATA #IMPLIED
|
||||||
textcolor_selected CDATA #IMPLIED
|
textcolor_selected CDATA #IMPLIED
|
||||||
text_align %align; #IMPLIED
|
text_align %align; #IMPLIED
|
||||||
text_valign %valign; #IMPLIED
|
text_valign %valign; #IMPLIED
|
||||||
tooltip CDATA #IMPLIED
|
tooltip CDATA #IMPLIED
|
||||||
tooltip_style CDATA #IMPLIED"
|
tooltip_style CDATA #IMPLIED"
|
||||||
>
|
>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
<objects>
|
<objects>
|
||||||
-->
|
-->
|
||||||
<!ELEMENT objects (script|object)*>
|
<!ELEMENT objects (script|object)*>
|
||||||
|
|
||||||
<!ELEMENT script (#PCDATA)>
|
<!ELEMENT script (#PCDATA)>
|
||||||
<!ATTLIST script
|
<!ATTLIST script
|
||||||
file CDATA #IMPLIED
|
file CDATA #IMPLIED
|
||||||
>
|
>
|
||||||
|
|
||||||
<!ELEMENT object (#PCDATA|object|action|item)*> <!-- 'item' is used by "list" and "dropdown" -->
|
<!ELEMENT object (#PCDATA|object|action|item)*> <!-- 'item' is used by "list" and "dropdown" -->
|
||||||
<!ATTLIST object
|
<!ATTLIST object
|
||||||
%unique_settings;
|
%unique_settings;
|
||||||
%base_settings;
|
%base_settings;
|
||||||
%ex_settings;
|
%ex_settings;
|
||||||
>
|
>
|
||||||
<!ELEMENT action (#PCDATA)>
|
<!ELEMENT action (#PCDATA)>
|
||||||
<!ATTLIST action
|
<!ATTLIST action
|
||||||
on CDATA #REQUIRED
|
on CDATA #REQUIRED
|
||||||
file CDATA #IMPLIED
|
file CDATA #IMPLIED
|
||||||
>
|
>
|
||||||
<!ELEMENT item (#PCDATA)>
|
<!ELEMENT item (#PCDATA)>
|
||||||
<!ATTLIST item
|
<!ATTLIST item
|
||||||
enabled %bool; #IMPLIED
|
enabled %bool; #IMPLIED
|
||||||
>
|
>
|
||||||
|
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
<styles>
|
<styles>
|
||||||
-->
|
-->
|
||||||
<!ELEMENT styles (style*)>
|
<!ELEMENT styles (style*)>
|
||||||
<!ELEMENT style EMPTY>
|
<!ELEMENT style EMPTY>
|
||||||
<!ATTLIST style
|
<!ATTLIST style
|
||||||
name CDATA #REQUIRED
|
name CDATA #REQUIRED
|
||||||
%base_settings;
|
%base_settings;
|
||||||
%ex_settings;
|
%ex_settings;
|
||||||
>
|
>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
<setup>
|
<setup>
|
||||||
-->
|
-->
|
||||||
<!ELEMENT setup (icon|scrollbar|tooltip|color)*>
|
<!ELEMENT setup (icon|scrollbar|tooltip|color)*>
|
||||||
<!ELEMENT scrollbar EMPTY>
|
<!ELEMENT scrollbar EMPTY>
|
||||||
<!ELEMENT icon EMPTY>
|
<!ELEMENT icon EMPTY>
|
||||||
<!ELEMENT tooltip EMPTY>
|
<!ELEMENT tooltip EMPTY>
|
||||||
<!ELEMENT color (#PCDATA)>
|
<!ELEMENT color (#PCDATA)>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
<scrollbar>
|
<scrollbar>
|
||||||
-->
|
-->
|
||||||
<!ATTLIST scrollbar
|
<!ATTLIST scrollbar
|
||||||
name CDATA #REQUIRED
|
name CDATA #REQUIRED
|
||||||
width CDATA #IMPLIED
|
width CDATA #IMPLIED
|
||||||
scroll_wheel %bool; #IMPLIED
|
scroll_wheel %bool; #IMPLIED
|
||||||
alwaysshown %bool; #IMPLIED
|
alwaysshown %bool; #IMPLIED
|
||||||
scroll_speed CDATA #IMPLIED
|
scroll_speed CDATA #IMPLIED
|
||||||
sprite_button_top CDATA #IMPLIED
|
sprite_button_top CDATA #IMPLIED
|
||||||
sprite_button_top_pressed CDATA #IMPLIED
|
sprite_button_top_pressed CDATA #IMPLIED
|
||||||
sprite_button_top_disabled CDATA #IMPLIED
|
sprite_button_top_disabled CDATA #IMPLIED
|
||||||
sprite_button_top_over CDATA #IMPLIED
|
sprite_button_top_over CDATA #IMPLIED
|
||||||
sprite_button_bottom CDATA #IMPLIED
|
sprite_button_bottom CDATA #IMPLIED
|
||||||
sprite_button_bottom_pressed CDATA #IMPLIED
|
sprite_button_bottom_pressed CDATA #IMPLIED
|
||||||
sprite_button_bottom_disabled CDATA #IMPLIED
|
sprite_button_bottom_disabled CDATA #IMPLIED
|
||||||
sprite_button_bottom_over CDATA #IMPLIED
|
sprite_button_bottom_over CDATA #IMPLIED
|
||||||
sprite_bar_vertical CDATA #IMPLIED
|
sprite_bar_vertical CDATA #IMPLIED
|
||||||
sprite_bar_vertical_over CDATA #IMPLIED
|
sprite_bar_vertical_over CDATA #IMPLIED
|
||||||
sprite_bar_vertical_pressed CDATA #IMPLIED
|
sprite_bar_vertical_pressed CDATA #IMPLIED
|
||||||
sprite_back_vertical CDATA #IMPLIED
|
sprite_back_vertical CDATA #IMPLIED
|
||||||
minimum_bar_size CDATA #IMPLIED
|
minimum_bar_size CDATA #IMPLIED
|
||||||
>
|
>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
<icon>
|
<icon>
|
||||||
-->
|
-->
|
||||||
<!ATTLIST icon
|
<!ATTLIST icon
|
||||||
name CDATA #REQUIRED
|
name CDATA #REQUIRED
|
||||||
size CDATA #REQUIRED
|
size CDATA #REQUIRED
|
||||||
sprite CDATA #REQUIRED
|
sprite CDATA #REQUIRED
|
||||||
cell_id CDATA #IMPLIED
|
cell_id CDATA #IMPLIED
|
||||||
>
|
>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
<tooltip>
|
<tooltip>
|
||||||
-->
|
-->
|
||||||
<!ATTLIST tooltip
|
<!ATTLIST tooltip
|
||||||
name CDATA #REQUIRED
|
name CDATA #REQUIRED
|
||||||
sprite CDATA #IMPLIED
|
sprite CDATA #IMPLIED
|
||||||
anchor CDATA #IMPLIED
|
anchor CDATA #IMPLIED
|
||||||
buffer_zone CDATA #IMPLIED
|
buffer_zone CDATA #IMPLIED
|
||||||
font CDATA #IMPLIED
|
font CDATA #IMPLIED
|
||||||
maxwidth CDATA #IMPLIED
|
maxwidth CDATA #IMPLIED
|
||||||
offset CDATA #IMPLIED
|
offset CDATA #IMPLIED
|
||||||
textcolor CDATA #IMPLIED
|
textcolor CDATA #IMPLIED
|
||||||
delay CDATA #IMPLIED
|
delay CDATA #IMPLIED
|
||||||
use_object CDATA #IMPLIED
|
use_object CDATA #IMPLIED
|
||||||
hide_object CDATA #IMPLIED
|
hide_object CDATA #IMPLIED
|
||||||
>
|
>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
<color>
|
<color>
|
||||||
-->
|
-->
|
||||||
<!ATTLIST color
|
<!ATTLIST color
|
||||||
name CDATA #REQUIRED
|
name CDATA #REQUIRED
|
||||||
>
|
>
|
||||||
|
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
<sprites>
|
<sprites>
|
||||||
-->
|
-->
|
||||||
<!ELEMENT sprites (sprite*)>
|
<!ELEMENT sprites (sprite*)>
|
||||||
<!ELEMENT sprite (effect?,image+)>
|
<!ELEMENT sprite (effect?,image+)>
|
||||||
<!ELEMENT image (effect?)>
|
<!ELEMENT image (effect?)>
|
||||||
<!ELEMENT effect EMPTY>
|
<!ELEMENT effect EMPTY>
|
||||||
|
|
||||||
<!ATTLIST sprite
|
<!ATTLIST sprite
|
||||||
name CDATA #REQUIRED
|
name CDATA #REQUIRED
|
||||||
>
|
>
|
||||||
|
|
||||||
<!ATTLIST image
|
<!ATTLIST image
|
||||||
texture CDATA #IMPLIED
|
texture CDATA #IMPLIED
|
||||||
size CDATA #IMPLIED
|
size CDATA #IMPLIED
|
||||||
texture_size CDATA #IMPLIED
|
texture_size CDATA #IMPLIED
|
||||||
real_texture_placement CDATA #IMPLIED
|
real_texture_placement CDATA #IMPLIED
|
||||||
cell_size CDATA #IMPLIED
|
cell_size CDATA #IMPLIED
|
||||||
backcolor CDATA #IMPLIED
|
backcolor CDATA #IMPLIED
|
||||||
bordercolor CDATA #IMPLIED
|
bordercolor CDATA #IMPLIED
|
||||||
border %bool; #IMPLIED
|
border %bool; #IMPLIED
|
||||||
z_level CDATA #IMPLIED
|
z_level CDATA #IMPLIED
|
||||||
>
|
>
|
||||||
|
|
||||||
<!ATTLIST effect
|
<!ATTLIST effect
|
||||||
add_color CDATA #IMPLIED
|
add_color CDATA #IMPLIED
|
||||||
multiply_color CDATA #IMPLIED
|
multiply_color CDATA #IMPLIED
|
||||||
grayscale CDATA #IMPLIED
|
grayscale CDATA #IMPLIED
|
||||||
>
|
>
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
function init()
|
function init(data)
|
||||||
{
|
{
|
||||||
|
var mapName = "map";
|
||||||
|
if (data && data.attribs)
|
||||||
|
mapName = data.attribs.map;
|
||||||
|
|
||||||
// Set to "hourglass" cursor.
|
// Set to "hourglass" cursor.
|
||||||
setCursor("cursor-wait");
|
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).
|
// Choose random concept art for loading screen background (should depend on the selected civ later when this is specified).
|
||||||
var sprite = "";
|
var sprite = "";
|
||||||
@ -19,10 +22,10 @@ function init()
|
|||||||
|
|
||||||
// janwas: main loop now sets progress / description, but that won't
|
// janwas: main loop now sets progress / description, but that won't
|
||||||
// happen until the first timeslice completes, so set initial values.
|
// happen until the first timeslice completes, so set initial values.
|
||||||
getGUIObjectByName ("ldTitleBar").caption = "Loading Scenario ...";
|
getGUIObjectByName ("ldTitleBar").caption = "Loading Scenario";
|
||||||
getGUIObjectByName ("ldProgressBarText").caption = "";
|
getGUIObjectByName ("ldProgressBarText").caption = "";
|
||||||
getGUIObjectByName ("ldProgressBar").caption = 0;
|
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).
|
// Pick a random tip of the day (each line is a separate tip).
|
||||||
var tipArray = readFileLines("gui/text/tips.txt");
|
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
|
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
|
// 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.
|
// Switch GUI from loading screen to game session.
|
||||||
if (Engine.IsNewSimulation())
|
Engine.SwitchGuiPage("page_session_new.xml");
|
||||||
Engine.SwitchGuiPage("page_session_new.xml");
|
|
||||||
else
|
|
||||||
Engine.SwitchGuiPage("page_session.xml");
|
|
||||||
|
|
||||||
// Restore default cursor.
|
// Restore default cursor.
|
||||||
setCursor ("arrow-default");
|
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[
|
<action on="Press"><![CDATA[
|
||||||
// Open Session Setup window.
|
// Open Session Setup window.
|
||||||
sessionType = "Skirmish"
|
Engine.PushGuiPage("page_gamesetup.xml", { type: "offline" });
|
||||||
openSessionSetup ("");
|
|
||||||
]]></action>
|
]]></action>
|
||||||
</object>
|
</object>
|
||||||
|
|
||||||
@ -145,7 +144,7 @@ Watch for updates or get involved in the development: http://wildfiregames.com/0
|
|||||||
>
|
>
|
||||||
<action on="Press"><![CDATA[
|
<action on="Press"><![CDATA[
|
||||||
// Open Multiplayer connection window.
|
// Open Multiplayer connection window.
|
||||||
openMainMenuSubWindow ("pgMPModeSel");
|
Engine.PushGuiPage("page_gamesetup_mp.xml");
|
||||||
]]></action>
|
]]></action>
|
||||||
</object>
|
</object>
|
||||||
|
|
||||||
@ -350,834 +349,6 @@ Watch for updates or get involved in the development: http://wildfiregames.com/0
|
|||||||
|
|
||||||
</object>
|
</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 -
|
- OPTIONS SCREEN -
|
||||||
|
@ -31,6 +31,24 @@ function getHotloadData()
|
|||||||
return { selection: g_Selection.selected };
|
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()
|
function onTick()
|
||||||
{
|
{
|
||||||
g_DevSettings.controlAll = getGUIObjectByName("devControlAll").checked;
|
g_DevSettings.controlAll = getGUIObjectByName("devControlAll").checked;
|
||||||
@ -92,6 +110,9 @@ function updatePlayerDisplay(simState)
|
|||||||
{
|
{
|
||||||
var playerState = simState.players[Engine.GetPlayerID()];
|
var playerState = simState.players[Engine.GetPlayerID()];
|
||||||
|
|
||||||
|
if (!playerState)
|
||||||
|
return;
|
||||||
|
|
||||||
getGUIObjectByName("resourceFood").caption = playerState.resourceCounts.food;
|
getGUIObjectByName("resourceFood").caption = playerState.resourceCounts.food;
|
||||||
getGUIObjectByName("resourceWood").caption = playerState.resourceCounts.wood;
|
getGUIObjectByName("resourceWood").caption = playerState.resourceCounts.wood;
|
||||||
getGUIObjectByName("resourceStone").caption = playerState.resourceCounts.stone;
|
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="bandbox" type="image" sprite="bandbox" ghost="true" hidden="true" z="100"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<object name="netStatus" type="text" style="netStatus" z="100" hidden="true"/>
|
||||||
|
|
||||||
</objects>
|
</objects>
|
||||||
|
@ -138,4 +138,8 @@
|
|||||||
<image backcolor="0 0 0 85"/>
|
<image backcolor="0 0 0 85"/>
|
||||||
</sprite>
|
</sprite>
|
||||||
|
|
||||||
|
<sprite name="netStatusBackground">
|
||||||
|
<image backcolor="0 0 0 195"/>
|
||||||
|
</sprite>
|
||||||
|
|
||||||
</sprites>
|
</sprites>
|
||||||
|
@ -103,4 +103,12 @@
|
|||||||
text_valign="center"
|
text_valign="center"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<style name="netStatus"
|
||||||
|
font="serif-bold-18"
|
||||||
|
textcolor="255 255 255"
|
||||||
|
text_align="center"
|
||||||
|
text_valign="center"
|
||||||
|
sprite="netStatusBackground"
|
||||||
|
/>
|
||||||
|
|
||||||
</styles>
|
</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()
|
Player.prototype.Init = function()
|
||||||
{
|
{
|
||||||
this.playerID = undefined;
|
this.playerID = undefined;
|
||||||
this.playerName = "Unknown";
|
this.name = "Unknown";
|
||||||
this.civ = "celt";
|
this.civ = "gaia";
|
||||||
|
this.colour = { "r": 0.0, "g": 0.0, "b": 0.0, "a": 1.0 };
|
||||||
this.popCount = 0;
|
this.popCount = 0;
|
||||||
this.popLimit = 50;
|
this.popLimit = 50;
|
||||||
this.resourceCount = {
|
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)
|
Player.prototype.SetPlayerID = function(id)
|
||||||
{
|
{
|
||||||
this.playerID = 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()
|
Player.prototype.GetPopulationCount = function()
|
||||||
{
|
{
|
||||||
return this.popCount;
|
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)
|
switch (cmd.type)
|
||||||
{
|
{
|
||||||
|
case "debug-print":
|
||||||
|
print(cmd.message);
|
||||||
|
break;
|
||||||
|
|
||||||
case "walk":
|
case "walk":
|
||||||
for each (var ent in cmd.entities)
|
for each (var ent in cmd.entities)
|
||||||
{
|
{
|
||||||
@ -120,7 +124,7 @@ function ProcessCommand(player, cmd)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
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
|
-- * defines: a table of symbols to define
|
||||||
extern_lib_defs = {
|
extern_lib_defs = {
|
||||||
boost = {
|
boost = {
|
||||||
unix_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" }
|
osx_names = { "boost_signals-mt", "boost_filesystem-mt", "boost_system-mt", "boost_random-mt" }
|
||||||
},
|
},
|
||||||
cryptopp = {
|
cryptopp = {
|
||||||
win_names = { "cryptopp" },
|
win_names = { "cryptopp" },
|
||||||
|
@ -3,7 +3,7 @@ PROJECT_NUMBER = trunk
|
|||||||
|
|
||||||
TAB_SIZE = 4
|
TAB_SIZE = 4
|
||||||
|
|
||||||
INPUT = ../../source/simulation2 ../../source/scriptinterface
|
INPUT = ../../source/simulation2 ../../source/scriptinterface ../../source/network
|
||||||
INCLUDE_PATH = ../../source
|
INCLUDE_PATH = ../../source
|
||||||
EXAMPLE_PATH = ../../source
|
EXAMPLE_PATH = ../../source
|
||||||
RECURSIVE = YES
|
RECURSIVE = YES
|
||||||
|
@ -244,7 +244,7 @@ void CGameViewImpl::ScriptingInit()
|
|||||||
CJSObject<CGameViewImpl>::ScriptingInit("GameView");
|
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.scroll.speed", Float, m->ViewScrollSpeed );
|
||||||
CFG_GET_SYS_VAL( "view.rotate.speed", Float, m->ViewRotateSensitivity );
|
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
|
// CGameView init
|
||||||
RegMemFun1(this, &CGameView::Initialize, pAttribs, L"CGameView init", 1);
|
RegMemFun(this, &CGameView::Initialize, L"CGameView init", 1);
|
||||||
|
|
||||||
// previously done by CGameView::InitResources
|
// previously done by CGameView::InitResources
|
||||||
RegMemFun(g_TexMan.GetSingletonPtr(), &CTextureManager::LoadTerrainTextures, L"LoadTerrainTextures", 60);
|
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
|
#include "lib/input.h" // InReaction - can't forward-declare enum
|
||||||
|
|
||||||
class CGame;
|
class CGame;
|
||||||
class CGameAttributes;
|
|
||||||
class CObjectManager;
|
class CObjectManager;
|
||||||
class CCamera;
|
class CCamera;
|
||||||
class CCinemaManager;
|
class CCinemaManager;
|
||||||
@ -67,8 +66,8 @@ public:
|
|||||||
|
|
||||||
void SetViewport(const SViewPort& vp);
|
void SetViewport(const SViewPort& vp);
|
||||||
|
|
||||||
void RegisterInit(CGameAttributes *pAttribs);
|
void RegisterInit();
|
||||||
int Initialize(CGameAttributes *pGameAttributes);
|
int Initialize();
|
||||||
|
|
||||||
CObjectManager& GetObjectManager() const;
|
CObjectManager& GetObjectManager() const;
|
||||||
|
|
||||||
|
@ -269,9 +269,12 @@ private:
|
|||||||
// # entities+nonentities processed and total (for progress calc)
|
// # entities+nonentities processed and total (for progress calc)
|
||||||
int completed_jobs, total_jobs;
|
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 Init(const VfsPath& xml_filename);
|
||||||
|
|
||||||
|
void ReadPlayers();
|
||||||
void ReadTerrain(XMBElement parent);
|
void ReadTerrain(XMBElement parent);
|
||||||
void ReadEnvironment(XMBElement parent);
|
void ReadEnvironment(XMBElement parent);
|
||||||
void ReadCamera(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
|
// 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"));
|
XMBElement ents = nodes.GetFirstNamedItem(xmb_file.GetElementID("Entities"));
|
||||||
XERO_ITER_EL(ents, ent)
|
XERO_ITER_EL(ents, ent)
|
||||||
{
|
{
|
||||||
utf16string uid = ent.GetAttributes().GetNamedItem(at_uid);
|
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
|
// Initialise player data
|
||||||
|
ReadPlayers();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void CXMLReader::ReadPlayers()
|
||||||
|
{
|
||||||
CmpPtr<ICmpPlayerManager> cmpPlayerMan(*m_MapReader.pSimulation2, SYSTEM_ENTITY);
|
CmpPtr<ICmpPlayerManager> cmpPlayerMan(*m_MapReader.pSimulation2, SYSTEM_ENTITY);
|
||||||
debug_assert(!cmpPlayerMan.null());
|
debug_assert(!cmpPlayerMan.null());
|
||||||
|
|
||||||
// TODO: this should be loaded from the XML instead
|
// TODO: we ought to read at least some of this data from the map file.
|
||||||
size_t numPlayers = 4;
|
// 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)
|
for (size_t i = 0; i < numPlayers; ++i)
|
||||||
{
|
{
|
||||||
int uid = ++max_uid;
|
int uid = ++max_uid;
|
||||||
entity_id_t ent = m_MapReader.pSimulation2->AddEntity(L"special/player", 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);
|
cmpPlayerMan->AddPlayer(ent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1013,10 +1063,6 @@ int CXMLReader::ReadOldEntities(XMBElement parent, double end_time)
|
|||||||
debug_warn(L"Invalid map XML data");
|
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
|
// The old version uses a flat entity naming system, so we need
|
||||||
// to translate it into the hierarchical filename
|
// 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)
|
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/MathUtil.h"
|
||||||
#include "maths/NUSpline.h"
|
#include "maths/NUSpline.h"
|
||||||
|
#include "ps/Game.h"
|
||||||
#include "ps/Loader.h"
|
#include "ps/Loader.h"
|
||||||
#include "ps/Player.h"
|
|
||||||
#include "ps/Filesystem.h"
|
#include "ps/Filesystem.h"
|
||||||
#include "ps/XML/XMLWriter.h"
|
#include "ps/XML/XMLWriter.h"
|
||||||
#include "renderer/SkyManager.h"
|
#include "renderer/SkyManager.h"
|
||||||
|
@ -17,9 +17,9 @@
|
|||||||
|
|
||||||
#include "precompiled.h"
|
#include "precompiled.h"
|
||||||
|
|
||||||
#include "lib/ogl.h"
|
|
||||||
#include "Material.h"
|
#include "Material.h"
|
||||||
#include "ps/Player.h"
|
|
||||||
|
#include "lib/ogl.h"
|
||||||
#include "ps/Game.h"
|
#include "ps/Game.h"
|
||||||
#include "ps/Overlay.h" // for CColor
|
#include "ps/Overlay.h" // for CColor
|
||||||
|
|
||||||
@ -106,12 +106,8 @@ SMaterialColor CMaterial::GetPlayerColor()
|
|||||||
|
|
||||||
if (m_PlayerID <= PLAYER_ID_LAST_VALID)
|
if (m_PlayerID <= PLAYER_ID_LAST_VALID)
|
||||||
{
|
{
|
||||||
CPlayer* player = g_Game->GetPlayer(m_PlayerID);
|
CColor c(g_Game->GetPlayerColour(m_PlayerID));
|
||||||
if (player)
|
return SMaterialColor(c.r, c.g, c.b, c.a);
|
||||||
{
|
|
||||||
const SPlayerColour& c (player->GetColour());
|
|
||||||
return SMaterialColor(c.r, c.g, c.b, c.a);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Oops, something failed.
|
// Oops, something failed.
|
||||||
|
@ -26,9 +26,6 @@
|
|||||||
#include "SkeletonAnimDef.h"
|
#include "SkeletonAnimDef.h"
|
||||||
#include "UnitAnimation.h"
|
#include "UnitAnimation.h"
|
||||||
|
|
||||||
#include "ps/Game.h"
|
|
||||||
#include "ps/Player.h"
|
|
||||||
|
|
||||||
CUnit::CUnit(CObjectEntry* object, CObjectManager& objectManager,
|
CUnit::CUnit(CObjectEntry* object, CObjectManager& objectManager,
|
||||||
const std::set<CStr>& actorSelections)
|
const std::set<CStr>& actorSelections)
|
||||||
: m_Object(object), m_Model(object->m_Model->Clone()),
|
: m_Object(object), m_Model(object->m_Model->Clone()),
|
||||||
|
@ -35,7 +35,8 @@
|
|||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
// CUnitManager constructor
|
// CUnitManager constructor
|
||||||
CUnitManager::CUnitManager()
|
CUnitManager::CUnitManager() :
|
||||||
|
m_ObjectManager(NULL)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,9 +33,7 @@
|
|||||||
#include "lib/bits.h"
|
#include "lib/bits.h"
|
||||||
#include "lib/timer.h"
|
#include "lib/timer.h"
|
||||||
#include "lib/sysdep/cpu.h"
|
#include "lib/sysdep/cpu.h"
|
||||||
#include "network/NetMessage.h"
|
|
||||||
#include "ps/Game.h"
|
#include "ps/Game.h"
|
||||||
#include "ps/Player.h"
|
|
||||||
#include "ps/Profile.h"
|
#include "ps/Profile.h"
|
||||||
#include "ps/World.h"
|
#include "ps/World.h"
|
||||||
#include "renderer/Renderer.h"
|
#include "renderer/Renderer.h"
|
||||||
|
@ -22,11 +22,14 @@
|
|||||||
#include "graphics/Camera.h"
|
#include "graphics/Camera.h"
|
||||||
#include "graphics/GameView.h"
|
#include "graphics/GameView.h"
|
||||||
#include "gui/GUIManager.h"
|
#include "gui/GUIManager.h"
|
||||||
|
#include "lib/sysdep/sysdep.h"
|
||||||
#include "maths/FixedVector3D.h"
|
#include "maths/FixedVector3D.h"
|
||||||
|
#include "network/NetClient.h"
|
||||||
|
#include "network/NetServer.h"
|
||||||
|
#include "network/NetTurnManager.h"
|
||||||
#include "ps/CLogger.h"
|
#include "ps/CLogger.h"
|
||||||
#include "ps/Game.h"
|
#include "ps/Game.h"
|
||||||
#include "ps/Overlay.h"
|
#include "ps/Overlay.h"
|
||||||
#include "ps/Player.h"
|
|
||||||
#include "ps/GameSetup/Config.h"
|
#include "ps/GameSetup/Config.h"
|
||||||
#include "simulation2/Simulation2.h"
|
#include "simulation2/Simulation2.h"
|
||||||
#include "simulation2/components/ICmpCommandQueue.h"
|
#include "simulation2/components/ICmpCommandQueue.h"
|
||||||
@ -34,6 +37,8 @@
|
|||||||
#include "simulation2/components/ICmpTemplateManager.h"
|
#include "simulation2/components/ICmpTemplateManager.h"
|
||||||
#include "simulation2/helpers/Selection.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
|
* This file defines a set of functions that are available to GUI scripts, to allow
|
||||||
* interaction with the rest of the engine.
|
* interaction with the rest of the engine.
|
||||||
@ -82,8 +87,8 @@ CScriptVal GuiInterfaceCall(void* cbdata, std::wstring name, CScriptVal data)
|
|||||||
return JSVAL_VOID;
|
return JSVAL_VOID;
|
||||||
|
|
||||||
int player = -1;
|
int player = -1;
|
||||||
if (g_Game && g_Game->GetLocalPlayer())
|
if (g_Game)
|
||||||
player = g_Game->GetLocalPlayer()->GetPlayerID();
|
player = g_Game->GetPlayerID();
|
||||||
|
|
||||||
CScriptValRooted arg (sim->GetScriptInterface().GetContext(), sim->GetScriptInterface().CloneValueFromOtherContext(guiManager->GetScriptInterface(), data.get()));
|
CScriptValRooted arg (sim->GetScriptInterface().GetContext(), sim->GetScriptInterface().CloneValueFromOtherContext(guiManager->GetScriptInterface(), data.get()));
|
||||||
CScriptVal ret (gui->ScriptCall(player, name, arg.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))
|
int GetPlayerID(void* UNUSED(cbdata))
|
||||||
{
|
{
|
||||||
if (g_Game && g_Game->GetLocalPlayer())
|
if (g_Game)
|
||||||
return g_Game->GetLocalPlayer()->GetPlayerID();
|
return g_Game->GetPlayerID();
|
||||||
return -1;
|
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
|
} // namespace
|
||||||
|
|
||||||
void GuiScriptingInit(ScriptInterface& scriptInterface)
|
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<std::vector<entity_id_t>, int, int, int, int, int, &PickFriendlyEntitiesInRect>("PickFriendlyEntitiesInRect");
|
||||||
scriptInterface.RegisterFunction<CFixedVector3D, int, int, &GetTerrainAtPoint>("GetTerrainAtPoint");
|
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
|
// Misc functions
|
||||||
scriptInterface.RegisterFunction<std::wstring, std::wstring, &SetCursor>("SetCursor");
|
scriptInterface.RegisterFunction<std::wstring, std::wstring, &SetCursor>("SetCursor");
|
||||||
scriptInterface.RegisterFunction<int, &GetPlayerID>("GetPlayerID");
|
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/external_libraries/sdl.h"
|
||||||
#include "lib/res/sound/snd_mgr.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/GameSetup.h"
|
||||||
#include "ps/GameSetup/Atlas.h"
|
#include "ps/GameSetup/Atlas.h"
|
||||||
#include "ps/GameSetup/Config.h"
|
#include "ps/GameSetup/Config.h"
|
||||||
#include "ps/GameSetup/CmdLineArgs.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 "ps/XML/Xeromyces.h"
|
||||||
#include "network/NetClient.h"
|
#include "network/NetClient.h"
|
||||||
#include "network/NetServer.h"
|
#include "network/NetServer.h"
|
||||||
#include "network/NetSession.h"
|
#include "network/NetSession.h"
|
||||||
#include "graphics/Camera.h"
|
#include "graphics/Camera.h"
|
||||||
#include "graphics/GameView.h"
|
#include "graphics/GameView.h"
|
||||||
|
#include "scripting/ScriptingHost.h"
|
||||||
#include "simulation2/Simulation2.h"
|
#include "simulation2/Simulation2.h"
|
||||||
#include "sound/CMusicPlayer.h"
|
#include "sound/CMusicPlayer.h"
|
||||||
#include "gui/GUIManager.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
|
// 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
|
// minimized or out of focus and relinquish the CPU a bit, in order to make
|
||||||
// debugging easier.
|
// debugging easier.
|
||||||
if( !g_NetClient && !g_NetServer && !g_app_has_focus )
|
if( !g_NetClient && !g_app_has_focus )
|
||||||
{
|
{
|
||||||
need_update = false;
|
need_update = false;
|
||||||
// don't use SDL_WaitEvent: don't want the main loop to freeze until app focus is restored
|
// 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/>.
|
* 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 "precompiled.h"
|
||||||
|
|
||||||
#include "NetClient.h"
|
#include "NetClient.h"
|
||||||
|
|
||||||
#include "NetJsEvents.h"
|
#include "NetMessage.h"
|
||||||
#include "Network.h"
|
#include "NetSession.h"
|
||||||
#include "NetServer.h"
|
#include "NetTurnManager.h"
|
||||||
|
|
||||||
#include "scripting/DOMEvent.h"
|
|
||||||
#include "scripting/JSConversions.h"
|
|
||||||
#include "scripting/ScriptableObject.h"
|
|
||||||
#include "ps/CConsole.h"
|
#include "ps/CConsole.h"
|
||||||
#include "ps/CLogger.h"
|
#include "ps/CLogger.h"
|
||||||
#include "ps/CStr.h"
|
#include "ps/CStr.h"
|
||||||
#include "ps/Game.h"
|
#include "ps/Game.h"
|
||||||
#include "ps/Globals.h"
|
#include "scriptinterface/ScriptInterface.h"
|
||||||
#include "ps/GameAttributes.h"
|
|
||||||
#include "simulation2/Simulation2.h"
|
#include "simulation2/Simulation2.h"
|
||||||
|
|
||||||
// DECLARATIONS
|
#include <boost/nondet_random.hpp>
|
||||||
|
#include <boost/random.hpp>
|
||||||
#define LOG_CATEGORY L"net"
|
|
||||||
|
|
||||||
CNetClient *g_NetClient = NULL;
|
CNetClient *g_NetClient = NULL;
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
CNetClient::CNetClient(CGame* game) :
|
||||||
// Name: CServerPlayer()
|
m_Session(NULL),
|
||||||
// Desc: Constructor
|
m_UserName(L"anonymous"),
|
||||||
//-----------------------------------------------------------------------------
|
m_GUID(GenerateGUID()), m_HostID((u32)-1), m_ClientTurnManager(NULL), m_Game(game)
|
||||||
CServerPlayer::CServerPlayer( uint sessionID, const CStr& nickname )
|
|
||||||
{
|
{
|
||||||
m_SessionID = sessionID;
|
m_Game->SetTurnManager(NULL); // delete the old local turn manager so we don't accidentally use it
|
||||||
m_Nickname = nickname;
|
|
||||||
}
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
void* context = this;
|
||||||
// Name: ~CServerPlayer()
|
|
||||||
// Desc: Destructor
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
CServerPlayer::~CServerPlayer( void )
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
// Set up transitions for session
|
||||||
// Name: ScriptingInit()
|
AddTransition(NCS_UNCONNECTED, (uint)NMT_CONNECT_COMPLETE, NCS_CONNECT, (void*)&OnConnect, context);
|
||||||
// Desc:
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
void CServerPlayer::ScriptingInit( void )
|
|
||||||
{
|
|
||||||
AddProperty(L"id", &CServerPlayer::m_SessionID, true);
|
|
||||||
AddProperty(L"name", &CServerPlayer::m_Nickname, true);
|
|
||||||
|
|
||||||
CJSObject<CServerPlayer>::ScriptingInit( "NetClient_ServerSession" );
|
AddTransition(NCS_CONNECT, (uint)NMT_SERVER_HANDSHAKE, NCS_HANDSHAKE, (void*)&OnHandshake, context);
|
||||||
}
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
AddTransition(NCS_HANDSHAKE, (uint)NMT_SERVER_HANDSHAKE_RESPONSE, NCS_AUTHENTICATE, (void*)&OnHandshakeResponse, context);
|
||||||
// Name: CNetClient()
|
|
||||||
// Desc: Constructor
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
CNetClient::CNetClient( ScriptInterface& scriptInterface, CGame* pGame, CGameAttributes* pGameAttribs )
|
|
||||||
: CNetHost( scriptInterface ), m_JsPlayers( &m_Players )
|
|
||||||
{
|
|
||||||
m_ClientTurnManager = NULL;
|
|
||||||
|
|
||||||
m_pLocalPlayerSlot = NULL;
|
AddTransition(NCS_AUTHENTICATE, (uint)NMT_AUTHENTICATE_RESULT, NCS_INITIAL_GAMESETUP, (void*)&OnAuthenticate, context);
|
||||||
m_pGame = pGame;
|
|
||||||
m_pGameAttributes = pGameAttribs;
|
|
||||||
|
|
||||||
g_ScriptingHost.SetGlobal("g_NetClient", OBJECT_TO_JSVAL(GetScript()));
|
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);
|
||||||
// Name: ~CNetClient()
|
AddTransition(NCS_PREGAME, (uint)NMT_PLAYER_ASSIGNMENT, NCS_PREGAME, (void*)&OnPlayerAssignment, context);
|
||||||
// Desc: Destructor
|
AddTransition(NCS_PREGAME, (uint)NMT_GAME_START, NCS_LOADING, (void*)&OnGameStart, context);
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
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();
|
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);
|
||||||
|
|
||||||
g_ScriptingHost.SetGlobal("g_NetClient", JSVAL_NULL);
|
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
|
||||||
// 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;
|
|
||||||
|
|
||||||
// Setup transitions for session
|
|
||||||
pSession->AddTransition( NCS_CONNECT, ( uint )NMT_SERVER_HANDSHAKE, NCS_HANDSHAKE, (void*)&OnHandshake, pContext );
|
|
||||||
|
|
||||||
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 );
|
|
||||||
|
|
||||||
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 );
|
|
||||||
|
|
||||||
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 );
|
|
||||||
|
|
||||||
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 );
|
|
||||||
|
|
||||||
// Set first state
|
// Set first state
|
||||||
pSession->SetFirstState( NCS_CONNECT );
|
SetFirstState(NCS_UNCONNECTED);
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
CNetClient::~CNetClient()
|
||||||
// Name: HandleConnect()
|
|
||||||
// Desc: Called when the client successfully connected to server
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
bool CNetClient::HandleConnect( CNetSession* pSession )
|
|
||||||
{
|
{
|
||||||
// Validate parameters
|
delete m_Session;
|
||||||
if ( !pSession ) return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
void CNetClient::SetUserName(const CStrW& username)
|
||||||
// Name: HandleDisconnect()
|
|
||||||
// Desc: Called when the client disconnected from the server
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
bool CNetClient::HandleDisconnect( CNetSession *pSession )
|
|
||||||
{
|
{
|
||||||
// Validate parameters
|
debug_assert(!m_Session); // must be called before we start the connection
|
||||||
if ( !pSession ) return false;
|
|
||||||
|
|
||||||
return true;
|
m_UserName = username;
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
bool CNetClient::SetupConnection(const CStr& server)
|
||||||
// Name: OnError()
|
|
||||||
// Desc:
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
bool CNetClient::OnError( void* pContext, CFsmEvent* pEvent )
|
|
||||||
{
|
{
|
||||||
// Validate parameters
|
CNetClientSessionRemote* session = new CNetClientSessionRemote(*this);
|
||||||
if ( !pEvent || !pContext ) return false;
|
bool ok = session->Connect(PS_DEFAULT_PORT, server);
|
||||||
|
SetAndOwnSession(session);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
// Error event?
|
void CNetClient::SetupLocalConnection(CNetServer& server)
|
||||||
if ( pEvent->GetType() != (uint)NMT_ERROR ) return true;
|
{
|
||||||
|
CNetClientSessionLocal* session = new CNetClientSessionLocal(*this, server);
|
||||||
|
SetAndOwnSession(session);
|
||||||
|
}
|
||||||
|
|
||||||
CNetClient* pClient = ( CNetClient* )( ( FsmActionCtx* )pContext )->pHost;
|
void CNetClient::SetAndOwnSession(CNetClientSession* session)
|
||||||
debug_assert( pClient );
|
{
|
||||||
|
delete m_Session;
|
||||||
|
m_Session = session;
|
||||||
|
}
|
||||||
|
|
||||||
CErrorMessage* pMessage = ( CErrorMessage* )pEvent->GetParamRef();
|
void CNetClient::Poll()
|
||||||
if ( pMessage )
|
{
|
||||||
|
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)
|
||||||
{
|
{
|
||||||
LOG( CLogger::Error, LOG_CATEGORY, L"CNetClient::OnError(): Error description %hs", pMessage->m_Error );
|
CScriptValRooted msg = GuiPoll();
|
||||||
|
if (msg.undefined())
|
||||||
|
break;
|
||||||
|
r += GetScriptInterface().ToString(msg.get()) + L"\n";
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
if ( pClient->m_OnConnectComplete.Defined() )
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CNetClient::OnHandshake(void* context, CFsmEvent* event)
|
||||||
|
{
|
||||||
|
debug_assert(event->GetType() == (uint)NMT_SERVER_HANDSHAKE);
|
||||||
|
|
||||||
|
CNetClient* client = (CNetClient*)context;
|
||||||
|
|
||||||
|
CCliHandshakeMessage handshake;
|
||||||
|
handshake.m_MagicResponse = PS_PROTOCOL_MAGIC_RESPONSE;
|
||||||
|
handshake.m_ProtocolVersion = PS_PROTOCOL_VERSION;
|
||||||
|
handshake.m_SoftwareVersion = PS_PROTOCOL_VERSION;
|
||||||
|
client->SendMessage(&handshake);
|
||||||
|
|
||||||
|
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_GUID = client->m_GUID;
|
||||||
|
authenticate.m_Name = client->m_UserName;
|
||||||
|
authenticate.m_Password = L""; // TODO
|
||||||
|
client->SendMessage(&authenticate);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CNetClient::OnAuthenticate(void* context, CFsmEvent* event)
|
||||||
|
{
|
||||||
|
debug_assert(event->GetType() == (uint)NMT_AUTHENTICATE_RESULT);
|
||||||
|
|
||||||
|
CNetClient* client = (CNetClient*)context;
|
||||||
|
|
||||||
|
CAuthenticateResultMessage* message = (CAuthenticateResultMessage*)event->GetParamRef();
|
||||||
|
|
||||||
|
LOGMESSAGE(L"Net: Authentication result: host=%d, %ls", message->m_HostID, message->m_Message.c_str() );
|
||||||
|
|
||||||
|
client->m_HostID = message->m_HostID;
|
||||||
|
|
||||||
|
CScriptValRooted msg;
|
||||||
|
client->GetScriptInterface().Eval("({'type':'netstatus','status':'authenticated'})", msg);
|
||||||
|
client->PushGuiMessage(msg);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CNetClient::OnGameSetup(void* context, CFsmEvent* event)
|
||||||
|
{
|
||||||
|
debug_assert(event->GetType() == (uint)NMT_GAME_SETUP);
|
||||||
|
|
||||||
|
CNetClient* client = (CNetClient*)context;
|
||||||
|
|
||||||
|
CGameSetupMessage* message = (CGameSetupMessage*)event->GetParamRef();
|
||||||
|
|
||||||
|
client->m_GameAttributes = message->m_Data;
|
||||||
|
|
||||||
|
CScriptValRooted msg;
|
||||||
|
client->GetScriptInterface().Eval("({'type':'gamesetup'})", msg);
|
||||||
|
client->GetScriptInterface().SetProperty(msg.get(), "data", message->m_Data, false);
|
||||||
|
client->PushGuiMessage(msg);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CNetClient::OnPlayerAssignment(void* context, CFsmEvent* event)
|
||||||
|
{
|
||||||
|
debug_assert(event->GetType() == (uint)NMT_PLAYER_ASSIGNMENT);
|
||||||
|
|
||||||
|
CNetClient* client = (CNetClient*)context;
|
||||||
|
|
||||||
|
CPlayerAssignmentMessage* message = (CPlayerAssignmentMessage*)event->GetParamRef();
|
||||||
|
|
||||||
|
// Unpack the message
|
||||||
|
client->m_PlayerAssignments.clear();
|
||||||
|
for (size_t i = 0; i < message->m_Hosts.size(); ++i)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
{
|
{
|
||||||
CConnectCompleteEvent connectComplete( ( CStrW )pMessage->m_Error, false );
|
CSimulationMessage* simMessage = static_cast<CSimulationMessage*> (message);
|
||||||
pClient->m_OnConnectComplete.DispatchEvent( pClient->GetScript(), &connectComplete );
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
CStr CNetClient::GenerateGUID()
|
||||||
// Name: OnPlayer()
|
|
||||||
// Desc:
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
bool CNetClient::OnPlayerJoin( void* pContext, CFsmEvent* pEvent )
|
|
||||||
{
|
{
|
||||||
// Validate parameters
|
// TODO: Ideally this will be guaranteed unique (and verified
|
||||||
if ( !pEvent || !pContext ) return false;
|
// 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.
|
||||||
|
|
||||||
// Connect event?
|
boost::random_device rng;
|
||||||
if ( pEvent->GetType() != NMT_PLAYER_JOIN ) return true;
|
boost::uniform_int<u32> dist(0, std::numeric_limits<u32>::max());
|
||||||
|
boost::variate_generator<boost::random_device&, boost::uniform_int<u32> > gen(rng, dist);
|
||||||
|
|
||||||
CNetClient* pClient = ( CNetClient* )( ( FsmActionCtx* )pContext )->pHost;
|
CStr guid;
|
||||||
debug_assert( pClient );
|
for (size_t i = 0; i < 2; ++i)
|
||||||
|
|
||||||
CPlayerJoinMessage* pMessage = ( CPlayerJoinMessage* )pEvent->GetParamRef();
|
|
||||||
if ( pMessage )
|
|
||||||
{
|
{
|
||||||
for ( uint i = 0; i < pMessage->m_Clients.size(); i++ )
|
char buf[32];
|
||||||
{
|
sprintf_s(buf, ARRAY_SIZE(buf), "%08X", gen());
|
||||||
pClient->OnPlayer( pMessage->m_Clients[ i ].m_SessionID, pMessage->m_Clients[ i ].m_Name );
|
guid += buf;
|
||||||
}
|
|
||||||
|
|
||||||
pClient->OnConnectComplete();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return guid;
|
||||||
}
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// 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;
|
|
||||||
|
|
||||||
case NMT_SERVER_HANDSHAKE_RESPONSE:
|
|
||||||
{
|
|
||||||
CAuthenticateMessage authenticate;
|
|
||||||
authenticate.m_Name = pClient->m_Nickname;
|
|
||||||
authenticate.m_Password = pClient->m_Password;
|
|
||||||
pClient->SendMessage( pSession, &authenticate );
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Name: OnAuthenticate()
|
|
||||||
// Desc:
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
bool CNetClient::OnAuthenticate( void* pContext, CFsmEvent* pEvent )
|
|
||||||
{
|
|
||||||
// Validate parameters
|
|
||||||
if ( !pEvent || !pContext ) return false;
|
|
||||||
|
|
||||||
CNetClient* pClient = ( CNetClient* )( ( FsmActionCtx* )pContext )->pHost;
|
|
||||||
UNUSED2(pClient);
|
|
||||||
CNetSession* pSession = ( ( FsmActionCtx* )pContext )->pSession;
|
|
||||||
|
|
||||||
debug_assert( pClient );
|
|
||||||
debug_assert( pSession );
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
LOG(CLogger::Error, LOG_CATEGORY, L"CNetClient::OnAuthenticate(): Authentication result: %ls", pMessage->m_Message.c_str() );
|
|
||||||
|
|
||||||
pSession->SetID( pMessage->m_SessionID );
|
|
||||||
|
|
||||||
LOG(CLogger::Error, LOG_CATEGORY, L"CNetClient::OnAuthenticate(): My session ID is %d", pMessage->m_SessionID);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Name: OnPreGame()
|
|
||||||
// Desc:
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
bool CNetClient::OnPreGame( void* pContext, CFsmEvent* pEvent )
|
|
||||||
{
|
|
||||||
// Validate parameters
|
|
||||||
if ( !pEvent || !pContext ) return false;
|
|
||||||
|
|
||||||
CNetClient* pClient = ( CNetClient* )( ( FsmActionCtx* )pContext )->pHost;
|
|
||||||
CNetSession* pSession = ( CNetSession* )( ( FsmActionCtx* )pContext )->pSession;
|
|
||||||
|
|
||||||
debug_assert( pClient );
|
|
||||||
debug_assert( pSession );
|
|
||||||
|
|
||||||
switch ( pEvent->GetType() )
|
|
||||||
{
|
|
||||||
case NMT_PLAYER_LEAVE:
|
|
||||||
{
|
|
||||||
CPlayerLeaveMessage* pMessage = ( CPlayerLeaveMessage* )pEvent->GetParamRef();
|
|
||||||
if ( !pMessage ) return false;
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Name: OnInGame()
|
|
||||||
// Desc:
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
bool CNetClient::OnInGame( void *pContext, CFsmEvent* pEvent )
|
|
||||||
{
|
|
||||||
// Validate parameters
|
|
||||||
if ( !pEvent || !pContext ) return false;
|
|
||||||
|
|
||||||
CNetClient* pClient = ( CNetClient* )( ( FsmActionCtx* )pContext )->pHost;
|
|
||||||
|
|
||||||
CNetMessage* pMessage = ( CNetMessage* )pEvent->GetParamRef();
|
|
||||||
if ( pMessage )
|
|
||||||
{
|
|
||||||
if (pMessage->GetType() == NMT_SIMULATION_COMMAND)
|
|
||||||
{
|
|
||||||
CSimulationMessage* simMessage = static_cast<CSimulationMessage*> (pMessage);
|
|
||||||
pClient->m_ClientTurnManager->OnSimulationMessage(simMessage);
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Name: OnChat()
|
|
||||||
// Desc:
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
bool CNetClient::OnChat( void* pContext, CFsmEvent* pEvent )
|
|
||||||
{
|
|
||||||
// Validate parameters
|
|
||||||
if ( !pEvent || !pContext ) return false;
|
|
||||||
|
|
||||||
CNetClient* pClient = ( CNetClient* )( ( FsmActionCtx* )pContext )->pHost;
|
|
||||||
|
|
||||||
if ( pEvent->GetType() == NMT_CHAT )
|
|
||||||
{
|
|
||||||
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 );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
@ -15,131 +15,195 @@
|
|||||||
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
* 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
|
#ifndef NETCLIENT_H
|
||||||
#define NETCLIENT_H
|
#define NETCLIENT_H
|
||||||
|
|
||||||
// INCLUDES
|
#include "network/fsm.h"
|
||||||
#include "NetSession.h"
|
#include "network/NetHost.h"
|
||||||
#include "NetHost.h"
|
#include "scriptinterface/ScriptVal.h"
|
||||||
#include "NetTurnManager.h"
|
|
||||||
#include "ps/CStr.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
|
enum
|
||||||
{
|
{
|
||||||
NCS_CONNECT = 200,
|
NCS_UNCONNECTED,
|
||||||
NCS_HANDSHAKE = 300,
|
NCS_CONNECT,
|
||||||
NCS_AUTHENTICATE = 400,
|
NCS_HANDSHAKE,
|
||||||
NCS_PREGAME = 500,
|
NCS_AUTHENTICATE,
|
||||||
NCS_INGAME = 600
|
NCS_INITIAL_GAMESETUP,
|
||||||
|
NCS_PREGAME,
|
||||||
|
NCS_LOADING,
|
||||||
|
NCS_INGAME
|
||||||
};
|
};
|
||||||
|
|
||||||
class CPlayerSlot;
|
/**
|
||||||
class CPlayer;
|
* Network client.
|
||||||
class CGame;
|
* This code is run by every player (including the host, if they are not
|
||||||
class CGameAttributes;
|
* a dedicated server).
|
||||||
class CServerPlayer;
|
* It provides an interface between the GUI, the network (via CNetClientSession),
|
||||||
|
* and the game (via CGame and CNetClientTurnManager).
|
||||||
typedef std::map< uint, CServerPlayer* > PlayerMap;
|
*/
|
||||||
|
class CNetClient : public CFsm
|
||||||
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>
|
|
||||||
{
|
{
|
||||||
NONCOPYABLE(CNetClient);
|
NONCOPYABLE(CNetClient);
|
||||||
|
|
||||||
public:
|
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 );
|
virtual ~CNetClient();
|
||||||
~CNetClient( void );
|
|
||||||
|
|
||||||
bool CreateSession ( void );
|
/**
|
||||||
void OnPlayer ( uint ID, const CStr& name );
|
* Set the user's name that will be displayed to all players.
|
||||||
void OnPlayerLeave ( uint ID );
|
* 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;
|
* Retrieves the next queued GUI message, and removes it from the queue.
|
||||||
CGameAttributes *m_pGameAttributes;
|
* 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;
|
* Add a message to the queue, to be read by GuiPoll.
|
||||||
CScriptObject m_OnChat;
|
* The script value must be in the GetScriptInterface() JS context.
|
||||||
CScriptObject m_OnConnectComplete;
|
*/
|
||||||
CScriptObject m_OnDisconnect;
|
void PushGuiMessage(const CScriptValRooted& message);
|
||||||
CScriptObject m_OnPlayerJoin;
|
|
||||||
CScriptObject m_OnPlayerLeave;
|
|
||||||
|
|
||||||
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 );
|
* Send a message to the server.
|
||||||
virtual bool HandleDisconnect ( CNetSession *pSession );
|
* @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:
|
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 );
|
* Take ownership of a session object, and use it for all network communication.
|
||||||
static bool OnHandshake ( void* pContext, CFsmEvent* pEvent );
|
*/
|
||||||
static bool OnAuthenticate ( void* pContext, CFsmEvent* pEvent );
|
void SetAndOwnSession(CNetClientSession* session);
|
||||||
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 );
|
|
||||||
|
|
||||||
|
/**
|
||||||
bool SetupConnection( JSContext *cx, uintN argc, jsval *argv );
|
* Push a message onto the GUI queue listing the current player assignments.
|
||||||
|
*/
|
||||||
|
void PostPlayerAssignmentsToScript();
|
||||||
|
|
||||||
PlayerMap m_Players; // List of online players
|
CGame *m_Game;
|
||||||
|
CStrW m_UserName;
|
||||||
|
|
||||||
|
/// 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;
|
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;
|
extern CNetClient *g_NetClient;
|
||||||
|
|
||||||
#endif // NETCLIENT_H
|
#endif // NETCLIENT_H
|
||||||
|
|
||||||
|
@ -16,364 +16,40 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "precompiled.h"
|
#include "precompiled.h"
|
||||||
#include "NetHost.h"
|
|
||||||
#include "NetSession.h"
|
|
||||||
#include "NetMessage.h"
|
|
||||||
|
|
||||||
|
#include "NetHost.h"
|
||||||
|
|
||||||
|
#include "network/NetMessage.h"
|
||||||
#include "ps/CLogger.h"
|
#include "ps/CLogger.h"
|
||||||
#include "simulation2/Simulation2.h"
|
|
||||||
|
|
||||||
#include <enet/enet.h>
|
#include <enet/enet.h>
|
||||||
|
|
||||||
static const int ENET_DEFAULT_CHANNEL = 0;
|
bool CNetHost::SendMessage(const CNetMessage* message, ENetPeer* peer, const char* peerName)
|
||||||
static const int CONNECT_TIMEOUT = 5000;
|
|
||||||
static const int DISCONNECT_TIMEOUT = 1000;
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Name: CNetHost()
|
|
||||||
// Desc: Constructor
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
CNetHost::CNetHost(ScriptInterface& scriptInterface) :
|
|
||||||
m_ScriptInterface(scriptInterface)
|
|
||||||
{
|
{
|
||||||
m_Host = NULL;
|
ENetPacket* packet = CreatePacket(message);
|
||||||
}
|
if (!packet)
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// 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)
|
|
||||||
return false;
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
ENetPacket* CNetHost::CreatePacket(const CNetMessage* message)
|
||||||
// Name: Create()
|
|
||||||
// Desc: Creates a server host
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
bool CNetHost::Create(u16 port, size_t maxPeers)
|
|
||||||
{
|
{
|
||||||
ENetAddress addr;
|
size_t size = message->GetSerializedLength();
|
||||||
|
|
||||||
// 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();
|
|
||||||
|
|
||||||
debug_assert(size); // else we'll fail when accessing the 0th element
|
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);
|
buffer.resize(size);
|
||||||
|
|
||||||
// Save message to internal buffer
|
// Save message to internal buffer
|
||||||
pMessage->Serialize(&buffer[0]);
|
message->Serialize(&buffer[0]);
|
||||||
|
|
||||||
// Create a reliable packet
|
// Create a reliable packet
|
||||||
ENetPacket* pPacket = enet_packet_create(&buffer[0], size, ENET_PACKET_FLAG_RELIABLE);
|
ENetPacket* packet = enet_packet_create(&buffer[0], size, ENET_PACKET_FLAG_RELIABLE);
|
||||||
if (!pPacket)
|
if (!packet)
|
||||||
return false;
|
LOGERROR(L"Net: Failed to construct packet");
|
||||||
|
|
||||||
// Let ENet send the message to peer
|
return packet;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
@ -18,130 +18,45 @@
|
|||||||
#ifndef NETHOST_H
|
#ifndef NETHOST_H
|
||||||
#define NETHOST_H
|
#define NETHOST_H
|
||||||
|
|
||||||
#include "fsm.h"
|
|
||||||
|
|
||||||
#include "ps/CStr.h"
|
#include "ps/CStr.h"
|
||||||
|
|
||||||
#include <vector>
|
/**
|
||||||
|
* @file
|
||||||
|
* Various declarations shared by networking code.
|
||||||
|
*/
|
||||||
|
|
||||||
typedef struct _ENetPeer ENetPeer;
|
typedef struct _ENetPeer ENetPeer;
|
||||||
|
typedef struct _ENetPacket ENetPacket;
|
||||||
typedef struct _ENetHost ENetHost;
|
typedef struct _ENetHost ENetHost;
|
||||||
class CNetSession;
|
|
||||||
class CNetHost;
|
|
||||||
class CNetMessage;
|
class CNetMessage;
|
||||||
class ScriptInterface;
|
|
||||||
|
|
||||||
struct PeerSession
|
struct PlayerAssignment
|
||||||
{
|
{
|
||||||
ENetPeer* pPeer;
|
CStrW m_Name; // player name
|
||||||
CNetSession* pSession;
|
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
|
class CNetHost
|
||||||
{
|
{
|
||||||
NONCOPYABLE(CNetHost);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
static const int DEFAULT_CHANNEL = 0;
|
||||||
CNetHost(ScriptInterface& scriptInterface);
|
|
||||||
virtual ~CNetHost();
|
|
||||||
|
|
||||||
bool Create();
|
|
||||||
bool Create(u16 port, size_t maxPeers);
|
|
||||||
void Shutdown();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates whether the host is currently a server
|
* Transmit a message to the given peer.
|
||||||
*
|
* @param message message to send
|
||||||
* @return Boolean indicating whether the host is a server
|
* @param peer peer to send to
|
||||||
|
* @param peerName name of peer for debug logs
|
||||||
|
* @return true on success, false on failure
|
||||||
*/
|
*/
|
||||||
virtual bool IsServer() const { return false; }
|
static bool SendMessage(const CNetMessage* message, ENetPeer* peer, const char* peerName);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the number of sessions for the host
|
* Construct an ENet packet by serialising the given message.
|
||||||
*
|
* @return NULL on failure
|
||||||
* @return The number of sessions
|
|
||||||
*/
|
*/
|
||||||
size_t GetSessionCount() const;
|
static ENetPacket* CreatePacket(const CNetMessage* message);
|
||||||
|
|
||||||
/**
|
|
||||||
* 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
|
|
||||||
* @return true on success, false on failure
|
|
||||||
*/
|
|
||||||
bool Connect(const CStr& host, u16 port);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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
|
|
||||||
*/
|
|
||||||
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
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // NETHOST_H
|
#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/>.
|
* 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 "precompiled.h"
|
||||||
#include "ps/CLogger.h"
|
|
||||||
#include "Network.h"
|
|
||||||
#include "NetMessage.h"
|
#include "NetMessage.h"
|
||||||
|
|
||||||
|
#include "ps/CLogger.h"
|
||||||
|
|
||||||
#include "ps/Game.h"
|
#include "ps/Game.h"
|
||||||
#include "simulation2/Simulation2.h"
|
#include "simulation2/Simulation2.h"
|
||||||
|
|
||||||
@ -36,9 +27,6 @@
|
|||||||
#define ALLNETMSGS_IMPLEMENT
|
#define ALLNETMSGS_IMPLEMENT
|
||||||
#include "NetMessages.h"
|
#include "NetMessages.h"
|
||||||
|
|
||||||
// DEFINES
|
|
||||||
#define LOG_CATEGORY L"net"
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Name: CNetMessage()
|
// Name: CNetMessage()
|
||||||
// Desc: Constructor
|
// Desc: Constructor
|
||||||
@ -46,7 +34,6 @@
|
|||||||
CNetMessage::CNetMessage( void )
|
CNetMessage::CNetMessage( void )
|
||||||
{
|
{
|
||||||
m_Type = NMT_INVALID;
|
m_Type = NMT_INVALID;
|
||||||
m_Dirty = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
@ -56,7 +43,6 @@ CNetMessage::CNetMessage( void )
|
|||||||
CNetMessage::CNetMessage( NetMessageType type )
|
CNetMessage::CNetMessage( NetMessageType type )
|
||||||
{
|
{
|
||||||
m_Type = type;
|
m_Type = type;
|
||||||
m_Dirty = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
@ -65,7 +51,6 @@ CNetMessage::CNetMessage( NetMessageType type )
|
|||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
CNetMessage::~CNetMessage( void )
|
CNetMessage::~CNetMessage( void )
|
||||||
{
|
{
|
||||||
m_Dirty = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
@ -159,19 +144,15 @@ CNetMessage* CNetMessageFactory::CreateMessage(const void* pData,
|
|||||||
switch ( header.GetType() )
|
switch ( header.GetType() )
|
||||||
{
|
{
|
||||||
case NMT_GAME_SETUP:
|
case NMT_GAME_SETUP:
|
||||||
pNewMessage = new CGameSetupMessage;
|
pNewMessage = new CGameSetupMessage(scriptInterface);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case NMT_ASSIGN_PLAYER_SLOT:
|
case NMT_PLAYER_ASSIGNMENT:
|
||||||
pNewMessage = new CAssignPlayerSlotMessage;
|
pNewMessage = new CPlayerAssignmentMessage;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case NMT_PLAYER_CONFIG:
|
case NMT_LOADED_GAME:
|
||||||
pNewMessage = new CPlayerConfigMessage;
|
pNewMessage = new CLoadedGameMessage;
|
||||||
break;
|
|
||||||
|
|
||||||
case NMT_PLAYER_JOIN:
|
|
||||||
pNewMessage = new CPlayerJoinMessage;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case NMT_SERVER_HANDSHAKE:
|
case NMT_SERVER_HANDSHAKE:
|
||||||
@ -182,14 +163,6 @@ CNetMessage* CNetMessageFactory::CreateMessage(const void* pData,
|
|||||||
pNewMessage = new CSrvHandshakeResponseMessage;
|
pNewMessage = new CSrvHandshakeResponseMessage;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case NMT_CONNECT_COMPLETE:
|
|
||||||
pNewMessage = new CConnectCompleteMessage;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NMT_ERROR:
|
|
||||||
pNewMessage = new CErrorMessage;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NMT_CLIENT_HANDSHAKE:
|
case NMT_CLIENT_HANDSHAKE:
|
||||||
pNewMessage = new CCliHandshakeMessage;
|
pNewMessage = new CCliHandshakeMessage;
|
||||||
break;
|
break;
|
||||||
@ -227,7 +200,7 @@ CNetMessage* CNetMessageFactory::CreateMessage(const void* pData,
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
LOG(CLogger::Error, LOG_CATEGORY, L"CNetMessageFactory::CreateMessage(): Unknown message received" );
|
LOGERROR(L"CNetMessageFactory::CreateMessage(): Unknown message type '%d' received", header.GetType());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -236,3 +209,18 @@ CNetMessage* CNetMessageFactory::CreateMessage(const void* pData,
|
|||||||
|
|
||||||
return pNewMessage;
|
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/>.
|
* 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
|
#ifndef NETMESSAGE_H
|
||||||
#define NETMESSAGE_H
|
#define NETMESSAGE_H
|
||||||
|
|
||||||
// INCLUDES
|
|
||||||
#include "Serialization.h"
|
#include "Serialization.h"
|
||||||
#include "ps/Vector2D.h"
|
|
||||||
|
|
||||||
#include <map>
|
|
||||||
|
|
||||||
// We need the enum from NetMessages.h, but we can't create any classes in
|
// 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
|
// NetMessages.h, since they in turn require CNetMessage to be defined
|
||||||
@ -38,13 +26,9 @@
|
|||||||
#include "NetMessages.h"
|
#include "NetMessages.h"
|
||||||
#undef ALLNETMSGS_DONT_CREATE_NMTS
|
#undef ALLNETMSGS_DONT_CREATE_NMTS
|
||||||
|
|
||||||
/*
|
/**
|
||||||
CLASS : CNetMessage
|
* The base class for all network messages exchanged within the game.
|
||||||
DESCRIPTION : CNetMessage is the base class for all network messages
|
|
||||||
exchanged within the game.
|
|
||||||
NOTES :
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class CNetMessage : public ISerializable
|
class CNetMessage : public ISerializable
|
||||||
{
|
{
|
||||||
NONCOPYABLE(CNetMessage);
|
NONCOPYABLE(CNetMessage);
|
||||||
@ -58,27 +42,11 @@ public:
|
|||||||
virtual ~CNetMessage( void );
|
virtual ~CNetMessage( void );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the message header. If changes are made on header,SetDirty
|
* Retrieves the message type.
|
||||||
* must be called on the header's message
|
* @return Message type
|
||||||
*
|
|
||||||
* @return Message header
|
|
||||||
*/
|
*/
|
||||||
//const CNetMessageHeader& GetHeader( void ) const { return m_Header; }
|
|
||||||
NetMessageType GetType( void ) const { return m_Type; }
|
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
|
* Serialize the message into the specified buffer parameter. The size
|
||||||
* required by the buffer parameter can be found by a call to
|
* 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 );
|
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
|
* Retrieves the size in bytes of the serialized message. Before calling
|
||||||
* Serialize, the memory size for the buffer where to serialize the message
|
* Serialize, the memory size for the buffer where to serialize the message
|
||||||
@ -136,7 +87,6 @@ public:
|
|||||||
virtual CStr ToString( void ) const;
|
virtual CStr ToString( void ) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool m_Dirty; // Message has been modified
|
|
||||||
NetMessageType m_Type; // Message type
|
NetMessageType m_Type; // Message type
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -159,13 +109,13 @@ public:
|
|||||||
*/
|
*/
|
||||||
static CNetMessage* CreateMessage( const void* pData, size_t dataSize, ScriptInterface& scriptInterface );
|
static CNetMessage* CreateMessage( const void* pData, size_t dataSize, ScriptInterface& scriptInterface );
|
||||||
|
|
||||||
private:
|
/**
|
||||||
|
* Clone a message object into a new scripting context.
|
||||||
// Not implemented
|
* @param message message to clone (can come from any script context)
|
||||||
CNetMessageFactory( void );
|
* @param scriptInterface script context to use for the new message
|
||||||
~CNetMessageFactory( void );
|
* @return new message, or NULL on failure
|
||||||
CNetMessageFactory( const CNetMessageFactory& );
|
*/
|
||||||
CNetMessageFactory& operator=( const CNetMessageFactory& );
|
static CNetMessage* CloneMessage( const CNetMessage* message, ScriptInterface& scriptInterface );
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -190,6 +140,24 @@ private:
|
|||||||
ScriptInterface& m_ScriptInterface;
|
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
|
// This time, the classes are created
|
||||||
#include "NetMessages.h"
|
#include "NetMessages.h"
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#include "NetMessage.h"
|
#include "NetMessage.h"
|
||||||
|
|
||||||
|
#include "lib/utf8.h"
|
||||||
#include "scriptinterface/ScriptInterface.h"
|
#include "scriptinterface/ScriptInterface.h"
|
||||||
#include "simulation2/serialization/BinarySerializer.h"
|
#include "simulation2/serialization/BinarySerializer.h"
|
||||||
#include "simulation2/serialization/StdDeserializer.h"
|
#include "simulation2/serialization/StdDeserializer.h"
|
||||||
@ -145,12 +146,58 @@ size_t CSimulationMessage::GetSerializedLength() const
|
|||||||
|
|
||||||
CStr CSimulationMessage::ToString() const
|
CStr CSimulationMessage::ToString() const
|
||||||
{
|
{
|
||||||
std::string source;
|
std::string source = utf8_from_wstring(m_ScriptInterface.ToString(m_Data.get()));
|
||||||
|
|
||||||
if (!m_ScriptInterface.CallFunction(m_Data.get(), "toSource", source))
|
|
||||||
source = "ERROR";
|
|
||||||
|
|
||||||
std::stringstream stream;
|
std::stringstream stream;
|
||||||
stream << "CSimulationMessage { m_Client: " << m_Client << ", m_Player: " << m_Player << ", m_Turn: " << m_Turn << ", m_Data: " << source << " }";
|
stream << "CSimulationMessage { m_Client: " << m_Client << ", m_Player: " << m_Player << ", m_Turn: " << m_Turn << ", m_Data: " << source << " }";
|
||||||
return stream.str();
|
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
|
||||||
* FILE : NetMessages.h
|
* The list of messages used by the network subsystem.
|
||||||
* PROJECT : 0 A.D.
|
|
||||||
* DESCRIPTION : The list of messages used by the network subsystem
|
|
||||||
*-----------------------------------------------------------------------------
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef NETMESSAGES_H
|
#ifndef NETMESSAGES_H
|
||||||
#define NETMESSAGES_H
|
#define NETMESSAGES_H
|
||||||
|
|
||||||
// INCLUDES
|
|
||||||
#include "ps/CStr.h"
|
#include "ps/CStr.h"
|
||||||
#include "scripting/JSSerialization.h"
|
|
||||||
#include "scriptinterface/ScriptVal.h"
|
#include "scriptinterface/ScriptVal.h"
|
||||||
|
|
||||||
// DEFINES
|
|
||||||
#define PS_PROTOCOL_MAGIC 0x5073013f // 'P', 's', 0x01, '?'
|
#define PS_PROTOCOL_MAGIC 0x5073013f // 'P', 's', 0x01, '?'
|
||||||
#define PS_PROTOCOL_MAGIC_RESPONSE 0x50630121 // 'P', 'c', 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'
|
#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
|
// The message types having a negative value are used internally and not sent
|
||||||
// over the network. The message types used for network communication have
|
// over the network. The message types used for network communication have
|
||||||
// positive values.
|
// positive values.
|
||||||
enum NetMessageType
|
enum NetMessageType
|
||||||
{
|
{
|
||||||
NMT_ERROR = -256, // Delivery of error states
|
NMT_CONNECT_COMPLETE = -256, // Connection is complete
|
||||||
NMT_CONNECT_COMPLETE, // Connection is complete
|
|
||||||
NMT_CLOSE_REQUEST, // Close connection request
|
|
||||||
NMT_INVALID = 0, // Invalid message
|
NMT_INVALID = 0, // Invalid message
|
||||||
NMT_SERVER_HANDSHAKE, // Handshake stage
|
NMT_SERVER_HANDSHAKE, // Handshake stage
|
||||||
NMT_CLIENT_HANDSHAKE,
|
NMT_CLIENT_HANDSHAKE,
|
||||||
@ -53,18 +45,11 @@ enum NetMessageType
|
|||||||
NMT_AUTHENTICATE, // Authentication stage
|
NMT_AUTHENTICATE, // Authentication stage
|
||||||
NMT_AUTHENTICATE_RESULT,
|
NMT_AUTHENTICATE_RESULT,
|
||||||
NMT_CHAT, // Common chat message
|
NMT_CHAT, // Common chat message
|
||||||
NMT_PLAYER_JOIN, // Pre-game stage
|
|
||||||
NMT_PLAYER_LEAVE,
|
|
||||||
NMT_GAME_SETUP,
|
NMT_GAME_SETUP,
|
||||||
NMT_ASSIGN_PLAYER_SLOT,
|
NMT_PLAYER_ASSIGNMENT,
|
||||||
NMT_PLAYER_CONFIG,
|
NMT_LOADED_GAME,
|
||||||
NMT_FILES_REQUIRED,
|
|
||||||
NMT_FILE_REQUEST,
|
|
||||||
NMT_FILE_CHUNK,
|
|
||||||
NMT_FILE_CHUNK_ACK,
|
|
||||||
NMT_FILE_PROGRESS,
|
|
||||||
NMT_GAME_START,
|
NMT_GAME_START,
|
||||||
NMT_END_COMMAND_BATCH, // In-game stage
|
NMT_END_COMMAND_BATCH,
|
||||||
NMT_SYNC_CHECK,
|
NMT_SYNC_CHECK,
|
||||||
NMT_SYNC_ERROR,
|
NMT_SYNC_ERROR,
|
||||||
NMT_SIMULATION_COMMAND,
|
NMT_SIMULATION_COMMAND,
|
||||||
@ -80,22 +65,6 @@ enum AuthenticateResultCode
|
|||||||
ARC_NICK_INVALID,
|
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
|
#endif // NETMESSAGES_H
|
||||||
|
|
||||||
#ifdef CREATING_NMT
|
#ifdef CREATING_NMT
|
||||||
@ -112,7 +81,7 @@ START_NMT_CLASS_(SrvHandshake, NMT_SERVER_HANDSHAKE)
|
|||||||
NMT_FIELD_INT(m_SoftwareVersion, u32, 4)
|
NMT_FIELD_INT(m_SoftwareVersion, u32, 4)
|
||||||
END_NMT_CLASS()
|
END_NMT_CLASS()
|
||||||
|
|
||||||
START_NMT_CLASS_(CliHandshake,NMT_CLIENT_HANDSHAKE)
|
START_NMT_CLASS_(CliHandshake, NMT_CLIENT_HANDSHAKE)
|
||||||
NMT_FIELD_INT(m_MagicResponse, u32, 4)
|
NMT_FIELD_INT(m_MagicResponse, u32, 4)
|
||||||
NMT_FIELD_INT(m_ProtocolVersion, u32, 4)
|
NMT_FIELD_INT(m_ProtocolVersion, u32, 4)
|
||||||
NMT_FIELD_INT(m_SoftwareVersion, u32, 4)
|
NMT_FIELD_INT(m_SoftwareVersion, u32, 4)
|
||||||
@ -125,14 +94,14 @@ START_NMT_CLASS_(SrvHandshakeResponse, NMT_SERVER_HANDSHAKE_RESPONSE)
|
|||||||
END_NMT_CLASS()
|
END_NMT_CLASS()
|
||||||
|
|
||||||
START_NMT_CLASS_(Authenticate, NMT_AUTHENTICATE)
|
START_NMT_CLASS_(Authenticate, NMT_AUTHENTICATE)
|
||||||
|
NMT_FIELD(CStr8, m_GUID)
|
||||||
NMT_FIELD(CStrW, m_Name)
|
NMT_FIELD(CStrW, m_Name)
|
||||||
//NMT_FIELD(CPasswordHash, m_Password)
|
|
||||||
NMT_FIELD(CStrW, m_Password)
|
NMT_FIELD(CStrW, m_Password)
|
||||||
END_NMT_CLASS()
|
END_NMT_CLASS()
|
||||||
|
|
||||||
START_NMT_CLASS_(AuthenticateResult, NMT_AUTHENTICATE_RESULT)
|
START_NMT_CLASS_(AuthenticateResult, NMT_AUTHENTICATE_RESULT)
|
||||||
NMT_FIELD_INT(m_Code, u32, 4)
|
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)
|
NMT_FIELD(CStrW, m_Message)
|
||||||
END_NMT_CLASS()
|
END_NMT_CLASS()
|
||||||
|
|
||||||
@ -142,36 +111,15 @@ START_NMT_CLASS_(Chat, NMT_CHAT)
|
|||||||
NMT_FIELD(CStrW, m_Message)
|
NMT_FIELD(CStrW, m_Message)
|
||||||
END_NMT_CLASS()
|
END_NMT_CLASS()
|
||||||
|
|
||||||
START_NMT_CLASS_(PlayerJoin, NMT_PLAYER_JOIN)
|
START_NMT_CLASS_(PlayerAssignment, NMT_PLAYER_ASSIGNMENT)
|
||||||
NMT_START_ARRAY(m_Clients)
|
NMT_START_ARRAY(m_Hosts)
|
||||||
NMT_FIELD_INT(m_SessionID, u32, 2)
|
NMT_FIELD(CStr8, m_GUID)
|
||||||
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)
|
|
||||||
NMT_FIELD(CStrW, m_Name)
|
NMT_FIELD(CStrW, m_Name)
|
||||||
NMT_FIELD(CStrW, m_Value)
|
NMT_FIELD_INT(m_PlayerID, u8, 1)
|
||||||
NMT_END_ARRAY()
|
NMT_END_ARRAY()
|
||||||
END_NMT_CLASS()
|
END_NMT_CLASS()
|
||||||
|
|
||||||
START_NMT_CLASS_(AssignPlayerSlot, NMT_ASSIGN_PLAYER_SLOT)
|
START_NMT_CLASS_(LoadedGame, NMT_LOADED_GAME)
|
||||||
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()
|
|
||||||
END_NMT_CLASS()
|
END_NMT_CLASS()
|
||||||
|
|
||||||
START_NMT_CLASS_(GameStart, NMT_GAME_START)
|
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/>.
|
* 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
|
#ifndef NETSERVER_H
|
||||||
#define NETSERVER_H
|
#define NETSERVER_H
|
||||||
|
|
||||||
// INCLUDES
|
|
||||||
#include "Network.h"
|
|
||||||
#include "NetHost.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>
|
#include <vector>
|
||||||
|
|
||||||
// DECLARATIONS
|
class CNetClientSessionLocal;
|
||||||
#define SERVER_SESSIONID 1
|
class CNetServerSession;
|
||||||
#define CLIENT_MIN_SESSIONID 100
|
class CNetServerTurnManager;
|
||||||
#define MAX_CLIENTS 8
|
class CFsmEvent;
|
||||||
#define MAX_OBSERVERS 5
|
class ScriptInterface;
|
||||||
#define DEFAULT_SERVER_NAME L"Noname Server"
|
class CPlayerAssignmentMessage;
|
||||||
#define DEFAULT_PLAYER_NAME L"Noname Player"
|
|
||||||
#define DEFAULT_WELCOME_MESSAGE L"Noname Server Welcome Message"
|
|
||||||
#define DEFAULT_HOST_PORT 0x5073
|
|
||||||
|
|
||||||
class CGameAttributes;
|
|
||||||
|
|
||||||
enum NetServerState
|
enum NetServerState
|
||||||
{
|
{
|
||||||
// We haven't opened the port yet, we're just setting some stuff up.
|
// 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
|
// 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
|
// 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
|
// rules are set up by the operator and where players join and select civs
|
||||||
// and stuff.
|
// and stuff.
|
||||||
SERVER_STATE_PREGAME,
|
SERVER_STATE_PREGAME,
|
||||||
|
|
||||||
|
// All the hosts are connected and are loading the game
|
||||||
|
SERVER_STATE_LOADING,
|
||||||
|
|
||||||
// The one with all the killing ;-)
|
// The one with all the killing ;-)
|
||||||
SERVER_STATE_INGAME,
|
SERVER_STATE_INGAME,
|
||||||
|
|
||||||
@ -71,214 +53,157 @@ enum NetServerState
|
|||||||
SERVER_STATE_POSTGAME
|
SERVER_STATE_POSTGAME
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Server session representation of client state
|
||||||
|
*/
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
NSS_HANDSHAKE = 1300,
|
NSS_HANDSHAKE,
|
||||||
NSS_AUTHENTICATE = 1400,
|
NSS_AUTHENTICATE,
|
||||||
NSS_PREGAME = 1500,
|
NSS_PREGAME,
|
||||||
NSS_INGAME = 1600
|
NSS_INGAME
|
||||||
};
|
};
|
||||||
|
|
||||||
enum
|
/**
|
||||||
{
|
* Network server.
|
||||||
NMT_APP_PLAYER_LEAVE = NMT_LAST + 100,
|
* Handles all the coordination between players.
|
||||||
NMT_APP_PREGAME = NMT_LAST + 200,
|
* One person runs this object, and every player (including the host) connects their CNetClient to it.
|
||||||
NMT_APP_OBSERVER = NMT_LAST + 300
|
*
|
||||||
};
|
* 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.
|
||||||
typedef std::map< uint, CNetSession* > IDSessionMap;
|
*
|
||||||
typedef std::vector< CNetSession* > SessionList;
|
* 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
|
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 :
|
|
||||||
*/
|
|
||||||
|
|
||||||
class CNetServer : public CNetHost,
|
|
||||||
public CJSObject<CNetServer>
|
|
||||||
{
|
{
|
||||||
NONCOPYABLE(CNetServer);
|
NONCOPYABLE(CNetServer);
|
||||||
public:
|
public:
|
||||||
|
CNetServer();
|
||||||
CNetServer( ScriptInterface& scriptInterface, CGame* pGame, CGameAttributes* pGameAttributes );
|
virtual ~CNetServer();
|
||||||
virtual ~CNetServer( void );
|
|
||||||
|
|
||||||
bool Start ( JSContext *pContext, uintN argc, jsval *argv );
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true indicating the host acts as a server
|
* Get the current server's connection/game state.
|
||||||
*
|
|
||||||
* @return Always true
|
|
||||||
*/
|
*/
|
||||||
virtual bool IsServer( void ) const { return true; }
|
NetServerState GetState() const { return m_State; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a new session to the list of sessions
|
* Begin listening for network connections.
|
||||||
*
|
* @return true on success, false on error (e.g. port already in use)
|
||||||
* @param pSession New session to add
|
|
||||||
*/
|
*/
|
||||||
void AddSession( CNetSession* pSession );
|
bool SetupConnection();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes the specified session from the list of sessions. If the session
|
* Poll the connections for messages from clients and process them, and send
|
||||||
* isn't found it returns NULL otherwise it returns the session object found.
|
* any queued messages.
|
||||||
*
|
* This must be called frequently (i.e. once per frame).
|
||||||
* @param pSession Session to remove
|
|
||||||
* @return The session object if found, NULL otherwise
|
|
||||||
*/
|
*/
|
||||||
CNetSession* RemoveSession( CNetSession* pSession );
|
virtual void Poll();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the session object for the specified ID
|
* Send a message to the given network peer.
|
||||||
*
|
|
||||||
* @param sessionID The session ID
|
|
||||||
* @return A pointer to session for the specified ID or
|
|
||||||
* NULL if not found
|
|
||||||
*/
|
*/
|
||||||
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:
|
protected:
|
||||||
|
/// Callback for autostart; called when a player has finished connecting
|
||||||
virtual bool SetupSession ( CNetSession* pSession );
|
virtual void OnAddPlayer() { }
|
||||||
virtual bool HandleConnect ( CNetSession* pSession );
|
/// Callback for autostart; called when a player has left the game
|
||||||
virtual bool HandleDisconnect ( CNetSession *pSession );
|
virtual void OnRemovePlayer() { }
|
||||||
|
|
||||||
private:
|
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
|
* Internal script context for (de)serializing script messages.
|
||||||
*
|
* (TODO: we shouldn't bother deserializing (except for debug printing of messages),
|
||||||
* @param pMessage Message where to load player properties
|
* we should just forward messages blindly and efficiently.)
|
||||||
* @param pPlayer Player for which we load the properties
|
|
||||||
*/
|
*/
|
||||||
void BuildPlayerConfigMessage(
|
ScriptInterface* m_ScriptInterface;
|
||||||
CPlayerConfigMessage* pMessage,
|
|
||||||
CPlayer* pPlayer );
|
|
||||||
|
|
||||||
/**
|
ENetHost* m_Host;
|
||||||
* Callback function used by the BuildPlayerSetupMessage to iterate over
|
std::vector<ENetPeer*> m_Peers;
|
||||||
* the player properties. It will be called for each property of the player
|
std::vector<CNetServerSession*> m_Sessions;
|
||||||
*
|
|
||||||
* @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 );
|
|
||||||
|
|
||||||
/**
|
std::vector<std::pair<CNetServerSession*, CNetMessage*> > m_LocalMessageQueue;
|
||||||
* Loads game properties into the specified message
|
|
||||||
*
|
|
||||||
* @param pMessage Message where to load game properties
|
|
||||||
*/
|
|
||||||
void BuildGameSetupMessage( CGameSetupMessage* pMessage );
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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;
|
||||||
* Retrieves a free session ID from the recycled sessions list
|
CStrW m_WelcomeMessage;
|
||||||
*
|
int m_Port;
|
||||||
* @return Free session ID
|
|
||||||
*/
|
|
||||||
uint GetFreeSessionID( void ) const;
|
|
||||||
|
|
||||||
IDSessionMap m_IDSessions; // List of connected ID and session pairs
|
u32 m_NextHostID;
|
||||||
|
|
||||||
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 );
|
|
||||||
|
|
||||||
CNetServerTurnManager* m_ServerTurnManager;
|
CNetServerTurnManager* m_ServerTurnManager;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Global network server for the standard game
|
||||||
extern CNetServer *g_NetServer;
|
extern CNetServer *g_NetServer;
|
||||||
|
|
||||||
#endif // NETSERVER_H
|
#endif // NETSERVER_H
|
||||||
|
@ -17,65 +17,228 @@
|
|||||||
|
|
||||||
#include "precompiled.h"
|
#include "precompiled.h"
|
||||||
#include "NetSession.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>
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
static const int CHANNEL_COUNT = 1;
|
||||||
// Name: CNetSession()
|
|
||||||
// Desc: Constructor
|
|
||||||
//-----------------------------------------------------------------------------
|
CNetClientSession::CNetClientSession(CNetClient& client) :
|
||||||
CNetSession::CNetSession(CNetHost* pHost, ENetPeer* pPeer)
|
m_Client(client)
|
||||||
{
|
{
|
||||||
m_Host = pHost;
|
|
||||||
m_Peer = pPeer;
|
|
||||||
m_ID = INVALID_SESSION;
|
|
||||||
m_PlayerSlot = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
CNetClientSession::~CNetClientSession()
|
||||||
// Name: ~CNetSession()
|
|
||||||
// Desc: Destructor
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
CNetSession::~CNetSession()
|
|
||||||
{
|
{
|
||||||
m_Peer = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Name: SetName()
|
|
||||||
// Desc: Set a new name for the session
|
CNetClientSessionRemote::CNetClientSessionRemote(CNetClient& client) :
|
||||||
//-----------------------------------------------------------------------------
|
CNetClientSession(client), m_Host(NULL), m_Server(NULL)
|
||||||
void CNetSession::SetName(const CStr& name)
|
|
||||||
{
|
{
|
||||||
m_Name = name;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
CNetClientSessionRemote::~CNetClientSessionRemote()
|
||||||
// Name: SetID()
|
|
||||||
// Desc: Set new ID for this session
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
void CNetSession::SetID(uint ID)
|
|
||||||
{
|
{
|
||||||
m_ID = ID;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
bool CNetClientSessionRemote::Connect(u16 port, const CStr& server)
|
||||||
// Name: SetPlayerSlot()
|
|
||||||
// Desc: Set the player slot for this session
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
void CNetSession::SetPlayerSlot(CPlayerSlot* pPlayerSlot)
|
|
||||||
{
|
{
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
void CNetClientSessionRemote::Disconnect()
|
||||||
// Name: ScriptingInit()
|
|
||||||
// Desc:
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
void CNetSession::ScriptingInit()
|
|
||||||
{
|
{
|
||||||
AddProperty(L"id", &CNetSession::m_ID);
|
debug_assert(m_Host && m_Server);
|
||||||
AddProperty(L"name", &CNetSession::m_Name);
|
|
||||||
|
|
||||||
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
|
#ifndef NETSESSION_H
|
||||||
#define NETSESSION_H
|
#define NETSESSION_H
|
||||||
|
|
||||||
#include "fsm.h"
|
#include "network/fsm.h"
|
||||||
#include "scripting/ScriptableObject.h"
|
#include "network/NetHost.h"
|
||||||
|
#include "ps/CStr.h"
|
||||||
|
#include "scriptinterface/ScriptVal.h"
|
||||||
|
|
||||||
class CPlayerSlot;
|
class CNetClient;
|
||||||
class CNetHost;
|
class CNetServer;
|
||||||
class CNetSession;
|
|
||||||
typedef struct _ENetPeer ENetPeer;
|
|
||||||
|
|
||||||
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;
|
NONCOPYABLE(CNetClientSession);
|
||||||
CNetSession* pSession;
|
|
||||||
|
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
|
* ENet-based implementation of CNetClientSession.
|
||||||
* 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.
|
|
||||||
*/
|
*/
|
||||||
|
class CNetClientSessionRemote : public CNetClientSession
|
||||||
class CNetSession : public CFsm,
|
|
||||||
public CJSObject<CNetSession>
|
|
||||||
{
|
{
|
||||||
NONCOPYABLE(CNetSession);
|
NONCOPYABLE(CNetClientSessionRemote);
|
||||||
|
|
||||||
friend class CNetHost;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
CNetClientSessionRemote(CNetClient& client);
|
||||||
|
~CNetClientSessionRemote();
|
||||||
|
|
||||||
virtual ~CNetSession();
|
bool Connect(u16 port, const CStr& server);
|
||||||
|
|
||||||
/**
|
virtual void Poll();
|
||||||
* Retrieves the name of the session
|
virtual void Disconnect();
|
||||||
*
|
virtual bool SendMessage(const CNetMessage* message);
|
||||||
* @return Session name
|
|
||||||
*/
|
|
||||||
const CStrW& GetName() const { return m_Name; }
|
|
||||||
|
|
||||||
/**
|
ENetPacket* CreatePacket(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();
|
|
||||||
|
|
||||||
private:
|
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
|
public:
|
||||||
ENetPeer* m_Peer; // Represents the peer host
|
CNetClientSessionLocal(CNetClient& client, CNetServer& server);
|
||||||
uint m_ID; // Session ID
|
|
||||||
CStrW m_Name; // Session name
|
void SetServerSession(CNetServerSessionLocal* session) { m_ServerSession = session; }
|
||||||
CPlayerSlot* m_PlayerSlot;
|
CNetServerSessionLocal* GetServerSession() { return m_ServerSession; }
|
||||||
FsmActionCtx m_FsmActionCtx;
|
|
||||||
|
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
|
#endif // NETSESSION_H
|
||||||
|
@ -19,11 +19,14 @@
|
|||||||
|
|
||||||
#include "NetTurnManager.h"
|
#include "NetTurnManager.h"
|
||||||
|
|
||||||
#include "NetServer.h"
|
#include "network/NetServer.h"
|
||||||
#include "NetClient.h"
|
#include "network/NetClient.h"
|
||||||
|
#include "network/NetMessage.h"
|
||||||
|
|
||||||
#include "gui/GUIManager.h"
|
#include "gui/GUIManager.h"
|
||||||
#include "maths/MathUtil.h"
|
#include "maths/MathUtil.h"
|
||||||
#include "ps/Profile.h"
|
#include "ps/Profile.h"
|
||||||
|
#include "ps/Pyrogenesis.h"
|
||||||
#include "simulation2/Simulation2.h"
|
#include "simulation2/Simulation2.h"
|
||||||
|
|
||||||
static const int TURN_LENGTH = 200; // TODO: this should be a variable controlled by the server depending on latency
|
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();
|
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_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.
|
// 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).
|
// 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);
|
m_QueuedCommands.resize(COMMAND_DELAY + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CNetTurnManager::SetPlayerID(int playerId)
|
||||||
|
{
|
||||||
|
m_PlayerId = playerId;
|
||||||
|
}
|
||||||
|
|
||||||
bool CNetTurnManager::Update(float frameLength)
|
bool CNetTurnManager::Update(float frameLength)
|
||||||
{
|
{
|
||||||
m_DeltaTime += frameLength;
|
m_DeltaTime += frameLength;
|
||||||
@ -186,8 +194,7 @@ void CNetClientTurnManager::PostCommand(CScriptValRooted data)
|
|||||||
|
|
||||||
// Transmit command to server
|
// Transmit command to server
|
||||||
CSimulationMessage msg(m_Simulation2.GetScriptInterface(), m_ClientId, m_PlayerId, m_CurrentTurn + COMMAND_DELAY, data.get());
|
CSimulationMessage msg(m_Simulation2.GetScriptInterface(), m_ClientId, m_PlayerId, m_CurrentTurn + COMMAND_DELAY, data.get());
|
||||||
CNetSession* session = m_NetClient.GetSession(0);
|
m_NetClient.SendMessage(&msg);
|
||||||
m_NetClient.SendMessage(session, &msg);
|
|
||||||
|
|
||||||
// Add to our local queue
|
// Add to our local queue
|
||||||
//AddCommand(m_ClientId, m_PlayerId, data, m_CurrentTurn + COMMAND_DELAY);
|
//AddCommand(m_ClientId, m_PlayerId, data, m_CurrentTurn + COMMAND_DELAY);
|
||||||
@ -204,8 +211,7 @@ void CNetClientTurnManager::NotifyFinishedOwnCommands(u32 turn)
|
|||||||
CEndCommandBatchMessage msg;
|
CEndCommandBatchMessage msg;
|
||||||
msg.m_TurnLength = TURN_LENGTH;
|
msg.m_TurnLength = TURN_LENGTH;
|
||||||
msg.m_Turn = turn;
|
msg.m_Turn = turn;
|
||||||
CNetSession* session = m_NetClient.GetSession(0);
|
m_NetClient.SendMessage(&msg);
|
||||||
m_NetClient.SendMessage(session, &msg);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CNetClientTurnManager::NotifyFinishedUpdate(u32 turn, const std::string& hash)
|
void CNetClientTurnManager::NotifyFinishedUpdate(u32 turn, const std::string& hash)
|
||||||
@ -218,8 +224,7 @@ void CNetClientTurnManager::NotifyFinishedUpdate(u32 turn, const std::string& ha
|
|||||||
CSyncCheckMessage msg;
|
CSyncCheckMessage msg;
|
||||||
msg.m_Turn = turn;
|
msg.m_Turn = turn;
|
||||||
msg.m_Hash = hash;
|
msg.m_Hash = hash;
|
||||||
CNetSession* session = m_NetClient.GetSession(0);
|
m_NetClient.SendMessage(&msg);
|
||||||
m_NetClient.SendMessage(session, &msg);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CNetClientTurnManager::OnSimulationMessage(CSimulationMessage* 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
|
// Add directly to the next turn, ignoring COMMAND_DELAY,
|
||||||
NETTURN_LOG(L"PostCommand()\n");
|
// because we don't need to compensate for network latency
|
||||||
#endif
|
AddCommand(m_ClientId, m_PlayerId, data, m_CurrentTurn + 1);
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CNetServerTurnManager::NotifyFinishedOwnCommands(u32 turn)
|
void CNetLocalTurnManager::NotifyFinishedOwnCommands(u32 turn)
|
||||||
{
|
{
|
||||||
#ifdef NETTURN_LOG
|
FinishedAllCommands(turn);
|
||||||
NETTURN_LOG(L"NotifyFinishedOwnCommands(%d)\n", turn);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
NotifyFinishedClientCommands(m_ClientId, 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)
|
void CNetServerTurnManager::NotifyFinishedClientCommands(int client, u32 turn)
|
||||||
@ -291,8 +293,7 @@ void CNetServerTurnManager::NotifyFinishedClientCommands(int client, u32 turn)
|
|||||||
msg.m_Turn = turn;
|
msg.m_Turn = turn;
|
||||||
m_NetServer.Broadcast(&msg);
|
m_NetServer.Broadcast(&msg);
|
||||||
|
|
||||||
// Move ourselves to the next turn
|
m_ReadyTurn = turn;
|
||||||
FinishedAllCommands(m_ReadyTurn + 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CNetServerTurnManager::NotifyFinishedClientUpdate(int client, u32 turn, const std::string& hash)
|
void CNetServerTurnManager::NotifyFinishedClientUpdate(int client, u32 turn, const std::string& hash)
|
||||||
@ -318,7 +319,7 @@ void CNetServerTurnManager::NotifyFinishedClientUpdate(int client, u32 turn, con
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
// Assume the host is correct (maybe we should choose the most common instead to help debugging)
|
// 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)
|
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;
|
msg.m_HashExpected = expected;
|
||||||
m_NetServer.Broadcast(&msg);
|
m_NetServer.Broadcast(&msg);
|
||||||
|
|
||||||
// Process it ourselves
|
|
||||||
OnSyncError(it->first, expected);
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -355,37 +353,3 @@ void CNetServerTurnManager::InitialiseClient(int client)
|
|||||||
|
|
||||||
// TODO: do we need some kind of UninitialiseClient in case they leave?
|
// 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.
|
* Each player performs the simulation for turn N.
|
||||||
* User input is translated into commands scheduled for execution in turn N+2 which are
|
* User input is translated into commands scheduled for execution in turn N+2 which are
|
||||||
* distributed to all other clients.
|
* 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.
|
* 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
|
* In that case, it does the simulation and tells all the other clients (via the server)
|
||||||
* sending commands for turn N+2, and it starts sending commands for turn N+3.
|
* 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.
|
* Commands are redistributed immediately by the server.
|
||||||
* To ensure a consistent execution of commands, they are each associated with a
|
* 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
|
class CNetTurnManager
|
||||||
{
|
{
|
||||||
NONCOPYABLE(CNetTurnManager);
|
NONCOPYABLE(CNetTurnManager);
|
||||||
public:
|
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() { }
|
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
|
* 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.
|
* turn length, the next turn is processed and the function returns true.
|
||||||
@ -127,11 +132,14 @@ protected:
|
|||||||
bool m_HasSyncError;
|
bool m_HasSyncError;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of CNetTurnManager for network clients.
|
||||||
|
*/
|
||||||
class CNetClientTurnManager : public CNetTurnManager
|
class CNetClientTurnManager : public CNetTurnManager
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CNetClientTurnManager(CSimulation2& simulation, CNetClient& client, int playerId, int clientId) :
|
CNetClientTurnManager(CSimulation2& simulation, CNetClient& client, int clientId) :
|
||||||
CNetTurnManager(simulation, playerId, clientId), m_NetClient(client)
|
CNetTurnManager(simulation, clientId), m_NetClient(client)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -147,11 +155,14 @@ protected:
|
|||||||
CNetClient& m_NetClient;
|
CNetClient& m_NetClient;
|
||||||
};
|
};
|
||||||
|
|
||||||
class CNetServerTurnManager : public CNetTurnManager
|
/**
|
||||||
|
* Implementation of CNetTurnManager for offline games.
|
||||||
|
*/
|
||||||
|
class CNetLocalTurnManager : public CNetTurnManager
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CNetServerTurnManager(CSimulation2& simulation, CNetServer& server, int playerId, int clientId) :
|
CNetLocalTurnManager(CSimulation2& simulation) :
|
||||||
CNetTurnManager(simulation, playerId, clientId), m_NetServer(server)
|
CNetTurnManager(simulation, 0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -159,6 +170,23 @@ public:
|
|||||||
|
|
||||||
virtual void PostCommand(CScriptValRooted data);
|
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 NotifyFinishedClientCommands(int client, u32 turn);
|
||||||
|
|
||||||
void NotifyFinishedClientUpdate(int client, u32 turn, const std::string& hash);
|
void NotifyFinishedClientUpdate(int client, u32 turn, const std::string& hash);
|
||||||
@ -166,9 +194,8 @@ public:
|
|||||||
void InitialiseClient(int client);
|
void InitialiseClient(int client);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void NotifyFinishedOwnCommands(u32 turn);
|
/// The latest turn for which we have received all commands from all clients
|
||||||
|
u32 m_ReadyTurn;
|
||||||
virtual void NotifyFinishedUpdate(u32 turn, const std::string& hash);
|
|
||||||
|
|
||||||
// Client ID -> ready turn number (the latest turn for which all commands have been received from that client)
|
// Client ID -> ready turn number (the latest turn for which all commands have been received from that client)
|
||||||
std::map<int, u32> m_ClientsReady;
|
std::map<int, u32> m_ClientsReady;
|
||||||
@ -183,22 +210,4 @@ protected:
|
|||||||
CNetServer& m_NetServer;
|
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
|
#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.
|
* This file is part of 0 A.D.
|
||||||
*
|
*
|
||||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
* 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.
|
* This file is part of 0 A.D.
|
||||||
*
|
*
|
||||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
* 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/>.
|
* 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
|
// INCLUDES
|
||||||
#include "precompiled.h"
|
#include "precompiled.h"
|
||||||
#include "fsm.h"
|
#include "fsm.h"
|
||||||
@ -306,7 +298,7 @@ CFsmTransition* CFsm::AddTransition(
|
|||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Name: AddTransition()
|
// Name: AddTransition()
|
||||||
// Desc: Adds a new transistion to the state machine
|
// Desc: Adds a new transition to the state machine
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
CFsmTransition* CFsm::AddTransition(
|
CFsmTransition* CFsm::AddTransition(
|
||||||
unsigned int state,
|
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.
|
* This file is part of 0 A.D.
|
||||||
*
|
*
|
||||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
* 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/>.
|
* 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
|
#ifndef FSM_H
|
||||||
#define FSM_H
|
#define FSM_H
|
||||||
|
|
||||||
@ -53,14 +45,11 @@ typedef std::map< unsigned int, CFsmEvent* > EventMap;
|
|||||||
typedef std::vector< CFsmTransition* > TransitionList;
|
typedef std::vector< CFsmTransition* > TransitionList;
|
||||||
typedef std::vector< CallbackFunction > CallbackList;
|
typedef std::vector< CallbackFunction > CallbackList;
|
||||||
|
|
||||||
/*
|
/**
|
||||||
CLASS : CFsmEvent
|
* Represents a signal in the state machine that a change has occurred.
|
||||||
DESCRIPTION : CFsmEvent class represents a signal in the state machine
|
* The CFsmEvent objects are under the control of CFsm so
|
||||||
that a change has occurred.
|
* they are created and deleted via CFsm.
|
||||||
NOTES : The CFsmEvent objects are under the control of CFsm so
|
*/
|
||||||
they are created and deleted via CFsm.
|
|
||||||
*/
|
|
||||||
|
|
||||||
class CFsmEvent
|
class CFsmEvent
|
||||||
{
|
{
|
||||||
NONCOPYABLE(CFsmEvent);
|
NONCOPYABLE(CFsmEvent);
|
||||||
@ -79,13 +68,9 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/*
|
/**
|
||||||
CLASS : CFsmTransition
|
* An association of event, condition, action and next state.
|
||||||
DESCRIPTION : The CFsmTransition class is an association of event, condition,
|
*/
|
||||||
action and next state.
|
|
||||||
NOTES :
|
|
||||||
*/
|
|
||||||
|
|
||||||
class CFsmTransition
|
class CFsmTransition
|
||||||
{
|
{
|
||||||
NONCOPYABLE(CFsmTransition);
|
NONCOPYABLE(CFsmTransition);
|
||||||
@ -118,18 +103,17 @@ private:
|
|||||||
CallbackList m_Conditions; // List of conditions for transition
|
CallbackList m_Conditions; // List of conditions for transition
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/**
|
||||||
CLASS : CFsm
|
* Manages states, events, actions and transitions
|
||||||
DESCRIPTION : CFsm manages states, events, actions and transitions
|
* between states. It provides an interface for advertising
|
||||||
between states. It provides an interface for advertising
|
* events and track the current state. The implementation is
|
||||||
events and track the current state. The implementation is
|
* a Mealy state machine, so the system respond to events
|
||||||
a Mealy state machine, so the system respond to events
|
* and execute some action.
|
||||||
and execute some action.
|
*
|
||||||
NOTES : A Mealy state machine has behaviour associated with state
|
* A Mealy state machine has behaviour associated with state
|
||||||
transitions; Mealy machines are event driven where an
|
* transitions; Mealy machines are event driven where an
|
||||||
event triggers a state transition
|
* event triggers a state transition
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class CFsm
|
class CFsm
|
||||||
{
|
{
|
||||||
NONCOPYABLE(CFsm);
|
NONCOPYABLE(CFsm);
|
||||||
@ -141,13 +125,11 @@ public:
|
|||||||
/**
|
/**
|
||||||
* Constructs the state machine. This method must be overriden so that
|
* Constructs the state machine. This method must be overriden so that
|
||||||
* connections are constructed for the particular state machine implemented
|
* connections are constructed for the particular state machine implemented
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
virtual void Setup( void );
|
virtual void Setup( void );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clear event, action and condition lists and reset state machine
|
* Clear event, action and condition lists and reset state machine
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
void Shutdown( void );
|
void Shutdown( void );
|
||||||
|
|
||||||
@ -177,8 +159,9 @@ public:
|
|||||||
bool IsValidEvent ( unsigned int eventType ) const;
|
bool IsValidEvent ( unsigned int eventType ) const;
|
||||||
virtual bool IsDone ( void ) const;
|
virtual bool IsDone ( void ) const;
|
||||||
|
|
||||||
private:
|
protected:
|
||||||
void SetCurrState ( unsigned int state );
|
void SetCurrState ( unsigned int state );
|
||||||
|
private:
|
||||||
bool IsFirstTime ( void ) const;
|
bool IsFirstTime ( void ) const;
|
||||||
|
|
||||||
bool m_Done; // FSM work is done
|
bool m_Done; // FSM work is done
|
||||||
|
@ -20,67 +20,14 @@
|
|||||||
#include "lib/external_libraries/sdl.h"
|
#include "lib/external_libraries/sdl.h"
|
||||||
#include "network/NetServer.h"
|
#include "network/NetServer.h"
|
||||||
#include "network/NetClient.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/Filesystem.h"
|
||||||
#include "ps/GameAttributes.h"
|
#include "ps/Loader.h"
|
||||||
#include "ps/XML/Xeromyces.h"
|
#include "ps/XML/Xeromyces.h"
|
||||||
#include "scriptinterface/ScriptInterface.h"
|
#include "scriptinterface/ScriptInterface.h"
|
||||||
|
#include "simulation2/Simulation2.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");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class TestNetComms : public CxxTest::TestSuite
|
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"", DataDir()/L"mods/public", VFS_MOUNT_MUST_EXIST));
|
||||||
TS_ASSERT_OK(g_VFS->Mount(L"cache", DataDir()/L"_testcache"));
|
TS_ASSERT_OK(g_VFS->Mount(L"cache", DataDir()/L"_testcache"));
|
||||||
CXeromyces::Startup();
|
CXeromyces::Startup();
|
||||||
|
|
||||||
new ScriptingHost;
|
|
||||||
CGameAttributes::ScriptingInit();
|
|
||||||
CNetServer::ScriptingInit();
|
|
||||||
CNetClient::ScriptingInit();
|
|
||||||
|
|
||||||
new CConfigDB;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void tearDown()
|
void tearDown()
|
||||||
{
|
{
|
||||||
delete &g_ConfigDB;
|
|
||||||
|
|
||||||
CGameAttributes::ScriptingShutdown();
|
|
||||||
CNetServer::ScriptingShutdown();
|
|
||||||
CNetClient::ScriptingShutdown();
|
|
||||||
delete &g_ScriptingHost;
|
|
||||||
|
|
||||||
CXeromyces::Terminate();
|
CXeromyces::Terminate();
|
||||||
g_VFS.reset();
|
g_VFS.reset();
|
||||||
DeleteDirectory(DataDir()/L"_testcache");
|
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));
|
for (size_t j = 0; j < clients.size(); ++j)
|
||||||
TS_ASSERT(client.Create());
|
if (clients[j]->GetCurrState() != state)
|
||||||
TS_ASSERT(client.ConnectAsync("127.0.0.1", DEFAULT_HOST_PORT));
|
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)
|
for (size_t i = 0; ; ++i)
|
||||||
{
|
{
|
||||||
debug_printf(L".");
|
// debug_printf(L".");
|
||||||
server.Poll();
|
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;
|
break;
|
||||||
|
|
||||||
if (i > 20)
|
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()
|
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;
|
TestStdoutLogger logger;
|
||||||
CGameAttributes gameAttributesClient;
|
|
||||||
TestNetServer server(scriptInterface, gameAttributesServer);
|
|
||||||
TestNetClient client(scriptInterface, gameAttributesClient);
|
|
||||||
connect(server, client);
|
|
||||||
client.CNetHost::Shutdown();
|
|
||||||
server.CNetHost::Shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TODO_test_destructor()
|
std::vector<CNetClient*> clients;
|
||||||
{
|
|
||||||
ScriptInterface scriptInterface("Engine");
|
|
||||||
|
|
||||||
CGameAttributes gameAttributesServer;
|
CGame client1Game(true);
|
||||||
CGameAttributes gameAttributesClient;
|
CGame client2Game(true);
|
||||||
TestNetServer server(scriptInterface, gameAttributesServer);
|
CGame client3Game(true);
|
||||||
TestNetClient client(scriptInterface, gameAttributesClient);
|
|
||||||
connect(server, client);
|
CNetServer server;
|
||||||
// run in Valgrind; this shouldn't leak
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
wait(server, clients, 100);
|
||||||
|
|
||||||
|
{
|
||||||
|
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)
|
void CConsole::SendChatMessage(const wchar_t *pText)
|
||||||
{
|
{
|
||||||
CNetHost *pHost = NULL;
|
if (g_NetClient)
|
||||||
|
|
||||||
if ( pHost )
|
|
||||||
{
|
{
|
||||||
CChatMessage chat;
|
// TODO
|
||||||
chat.m_Recipient = CHAT_RECIPIENT_ALL;
|
// g_NetClient3->SendChatMessage(pText);
|
||||||
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 );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,6 +136,8 @@ CLogger::~CLogger ()
|
|||||||
void CLogger::WriteMessage(const wchar_t* message)
|
void CLogger::WriteMessage(const wchar_t* message)
|
||||||
{
|
{
|
||||||
++m_NumberOfMessages;
|
++m_NumberOfMessages;
|
||||||
|
// if (m_UseDebugPrintf)
|
||||||
|
// debug_printf(L"MESSAGE: %ls\n", message);
|
||||||
|
|
||||||
*m_MainLog << L"<p>" << message << L"</p>\n";
|
*m_MainLog << L"<p>" << message << L"</p>\n";
|
||||||
m_MainLog->flush();
|
m_MainLog->flush();
|
||||||
@ -354,7 +356,6 @@ void CLogger::PushRenderMessage(ELogMethod method, const wchar_t* message)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void CLogger::CleanupRenderQueue()
|
void CLogger::CleanupRenderQueue()
|
||||||
{
|
{
|
||||||
if (m_RenderMessages.empty())
|
if (m_RenderMessages.empty())
|
||||||
@ -397,3 +398,15 @@ std::wstring TestLogger::GetOutput()
|
|||||||
{
|
{
|
||||||
return m_Stream.str();
|
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;
|
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
|
#endif
|
||||||
|
@ -29,20 +29,23 @@
|
|||||||
#include "graphics/UnitManager.h"
|
#include "graphics/UnitManager.h"
|
||||||
#include "lib/timer.h"
|
#include "lib/timer.h"
|
||||||
#include "network/NetClient.h"
|
#include "network/NetClient.h"
|
||||||
|
#include "network/NetServer.h"
|
||||||
|
#include "network/NetTurnManager.h"
|
||||||
#include "ps/CConsole.h"
|
#include "ps/CConsole.h"
|
||||||
#include "ps/CLogger.h"
|
#include "ps/CLogger.h"
|
||||||
#include "ps/CStr.h"
|
#include "ps/CStr.h"
|
||||||
#include "ps/GameAttributes.h"
|
|
||||||
#include "ps/Loader.h"
|
#include "ps/Loader.h"
|
||||||
|
#include "ps/Overlay.h"
|
||||||
#include "ps/Profile.h"
|
#include "ps/Profile.h"
|
||||||
#include "ps/World.h"
|
#include "ps/World.h"
|
||||||
|
#include "scripting/ScriptingHost.h"
|
||||||
|
#include "scriptinterface/ScriptInterface.h"
|
||||||
#include "simulation2/Simulation2.h"
|
#include "simulation2/Simulation2.h"
|
||||||
|
#include "simulation2/components/ICmpPlayer.h"
|
||||||
|
#include "simulation2/components/ICmpPlayerManager.h"
|
||||||
|
|
||||||
#include "gui/GUIManager.h"
|
#include "gui/GUIManager.h"
|
||||||
|
|
||||||
class CNetServer;
|
|
||||||
extern CNetServer *g_NetServer;
|
|
||||||
|
|
||||||
extern bool g_GameRestarted;
|
extern bool g_GameRestarted;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -54,18 +57,19 @@ CGame *g_Game=NULL;
|
|||||||
* Constructor
|
* Constructor
|
||||||
*
|
*
|
||||||
**/
|
**/
|
||||||
CGame::CGame():
|
CGame::CGame(bool disableGraphics):
|
||||||
m_World(new CWorld(this)),
|
m_World(new CWorld(this)),
|
||||||
m_Simulation2(new CSimulation2(&m_World->GetUnitManager(), m_World->GetTerrain())),
|
m_Simulation2(new CSimulation2(&m_World->GetUnitManager(), m_World->GetTerrain())),
|
||||||
m_GameView(new CGameView(this)),
|
m_GameView(disableGraphics ? NULL : new CGameView(this)),
|
||||||
m_pLocalPlayer(NULL),
|
|
||||||
m_GameStarted(false),
|
m_GameStarted(false),
|
||||||
m_Paused(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
|
// Need to set the CObjectManager references after various objects have
|
||||||
// been initialised, so do it here rather than via the initialisers above.
|
// been initialised, so do it here rather than via the initialisers above.
|
||||||
m_World->GetUnitManager().SetObjectManager(m_GameView->GetObjectManager());
|
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
|
m_TurnManager = new CNetLocalTurnManager(*m_Simulation2); // this will get replaced if we're a net server/client
|
||||||
|
|
||||||
@ -83,7 +87,8 @@ CGame::CGame():
|
|||||||
CGame::~CGame()
|
CGame::~CGame()
|
||||||
{
|
{
|
||||||
// Again, the in-game call tree is going to be different to the main menu one.
|
// Again, the in-game call tree is going to be different to the main menu one.
|
||||||
g_Profiler.StructuralReset();
|
if (CProfileManager::IsInitialised())
|
||||||
|
g_Profiler.StructuralReset();
|
||||||
|
|
||||||
delete m_TurnManager;
|
delete m_TurnManager;
|
||||||
delete m_GameView;
|
delete m_GameView;
|
||||||
@ -95,7 +100,11 @@ void CGame::SetTurnManager(CNetTurnManager* turnManager)
|
|||||||
{
|
{
|
||||||
if (m_TurnManager)
|
if (m_TurnManager)
|
||||||
delete m_TurnManager;
|
delete m_TurnManager;
|
||||||
|
|
||||||
m_TurnManager = turnManager;
|
m_TurnManager = turnManager;
|
||||||
|
|
||||||
|
if (m_TurnManager)
|
||||||
|
m_TurnManager->SetPlayerID(m_PlayerID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -103,24 +112,26 @@ void CGame::SetTurnManager(CNetTurnManager* turnManager)
|
|||||||
* Initializes the game with the set of attributes provided.
|
* Initializes the game with the set of attributes provided.
|
||||||
* Makes calls to initialize the game view, world, and simulation objects.
|
* Makes calls to initialize the game view, world, and simulation objects.
|
||||||
* Calls are made to facilitate progress reporting of the initialization.
|
* 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();
|
LDR_BeginRegistering();
|
||||||
|
|
||||||
// RC, 040804 - GameView needs to be initialized before World, otherwise GameView initialization
|
// RC, 040804 - GameView needs to be initialized before World, otherwise GameView initialization
|
||||||
// overwrites anything stored in the map file that gets loaded by CWorld::Initialize with default
|
// overwrites anything stored in the map file that gets loaded by CWorld::Initialize with default
|
||||||
// values. At the minute, it's just lighting settings, but could be extended to store camera position.
|
// 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
|
// 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?
|
// some point to be stored in the world object?
|
||||||
m_GameView->RegisterInit(pAttribs);
|
if (m_GameView)
|
||||||
m_World->RegisterInit(pAttribs);
|
m_GameView->RegisterInit();
|
||||||
|
m_World->RegisterInit(mapFile);
|
||||||
LDR_EndRegistering();
|
LDR_EndRegistering();
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Game initialization has been completed. Set game started flag and start the session.
|
* 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()
|
PSRETURN CGame::ReallyStartGame()
|
||||||
{
|
{
|
||||||
// Call the reallyStartGame GUI function, but only if it exists
|
// Call the reallyStartGame GUI function, but only if it exists
|
||||||
if (g_GUI->HasPages())
|
if (g_GUI && g_GUI->HasPages())
|
||||||
{
|
{
|
||||||
jsval fval, rval;
|
jsval fval, rval;
|
||||||
JSBool ok = JS_GetProperty(g_ScriptingHost.getContext(), g_GUI->GetScriptObject(), "reallyStartGame", &fval);
|
JSBool ok = JS_GetProperty(g_ScriptingHost.getContext(), g_GUI->GetScriptObject(), "reallyStartGame", &fval);
|
||||||
@ -138,11 +149,15 @@ PSRETURN CGame::ReallyStartGame()
|
|||||||
ok = JS_CallFunctionValue(g_ScriptingHost.getContext(), g_GUI->GetScriptObject(), fval, 0, NULL, &rval);
|
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");
|
debug_printf(L"GAME STARTED, ALL INIT COMPLETE\n");
|
||||||
m_GameStarted=true;
|
m_GameStarted=true;
|
||||||
|
|
||||||
// The call tree we've built for pregame probably isn't useful in-game.
|
// The call tree we've built for pregame probably isn't useful in-game.
|
||||||
g_Profiler.StructuralReset();
|
if (CProfileManager::IsInitialised())
|
||||||
|
g_Profiler.StructuralReset();
|
||||||
|
|
||||||
// Mark terrain as modified so the minimap can repaint (is there a cleaner way of handling this?)
|
// Mark terrain as modified so the minimap can repaint (is there a cleaner way of handling this?)
|
||||||
g_GameRestarted = true;
|
g_GameRestarted = true;
|
||||||
@ -150,56 +165,37 @@ PSRETURN CGame::ReallyStartGame()
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
void CGame::ChangeNetStatus(ENetStatus status)
|
||||||
* 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)
|
|
||||||
{
|
{
|
||||||
try
|
if (g_GUI && g_GUI->HasPages())
|
||||||
{
|
{
|
||||||
// JW: this loop is taken from ScEd and fixes lack of player color.
|
const char* statusStr = "?";
|
||||||
// TODO: determine proper number of players.
|
switch (status)
|
||||||
// SB: Only do this for non-network games
|
|
||||||
if (!g_NetClient && !g_NetServer)
|
|
||||||
{
|
{
|
||||||
for (int i=1; i<8; ++i)
|
case NET_WAITING_FOR_CONNECT: statusStr = "waiting_for_connect"; break;
|
||||||
pAttribs->GetSlot(i)->AssignLocal();
|
case NET_NORMAL: statusStr = "normal"; break;
|
||||||
}
|
}
|
||||||
|
|
||||||
pAttribs->FinalizeSlots();
|
g_GUI->GetScriptInterface().CallFunctionVoid(OBJECT_TO_JSVAL(g_GUI->GetScriptObject()), "changeNetStatus", statusStr);
|
||||||
m_NumPlayers=pAttribs->GetSlotCount();
|
|
||||||
|
|
||||||
// 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)
|
|
||||||
{
|
|
||||||
// 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];
|
|
||||||
}
|
|
||||||
|
|
||||||
RegisterInit(pAttribs);
|
|
||||||
}
|
}
|
||||||
catch (PSERROR_Game& e)
|
|
||||||
{
|
|
||||||
return e.getCode();
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int CGame::GetPlayerID()
|
||||||
|
{
|
||||||
|
return m_PlayerID;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CGame::SetPlayerID(int playerID)
|
||||||
|
{
|
||||||
|
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,
|
// TODO: doInterpolate is optional because Atlas interpolates explicitly,
|
||||||
// so that it has more control over the update rate. The game might want to
|
// 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)
|
if (m_Paused)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
if (!m_TurnManager)
|
||||||
|
return true;
|
||||||
|
|
||||||
deltaTime *= m_SimRate;
|
deltaTime *= m_SimRate;
|
||||||
|
|
||||||
bool ok = true;
|
bool ok = true;
|
||||||
@ -240,6 +239,9 @@ bool CGame::Update(double deltaTime, bool doInterpolate)
|
|||||||
|
|
||||||
void CGame::Interpolate(float frameLength)
|
void CGame::Interpolate(float frameLength)
|
||||||
{
|
{
|
||||||
|
if (!m_TurnManager)
|
||||||
|
return;
|
||||||
|
|
||||||
m_TurnManager->Interpolate(frameLength);
|
m_TurnManager->Interpolate(frameLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -312,32 +314,15 @@ void CGame::EndGame()
|
|||||||
break;
|
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)
|
static CColor BrokenColor(0.3f, 0.3f, 0.3f, 1.0f);
|
||||||
return m_Players[0];
|
CColor CGame::GetPlayerColour(int player) const
|
||||||
else
|
{
|
||||||
return NULL; // the caller will probably crash because of this,
|
CmpPtr<ICmpPlayerManager> cmpPlayerManager(*m_Simulation2, SYSTEM_ENTITY);
|
||||||
// but at least we've reported the error
|
if (cmpPlayerManager.null())
|
||||||
}
|
return BrokenColor;
|
||||||
else
|
CmpPtr<ICmpPlayer> cmpPlayer(*m_Simulation2, cmpPlayerManager->GetPlayerByID(player));
|
||||||
return m_Players[idx];
|
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.
|
* This file is part of 0 A.D.
|
||||||
*
|
*
|
||||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||||
@ -30,15 +30,9 @@
|
|||||||
class CWorld;
|
class CWorld;
|
||||||
class CSimulation2;
|
class CSimulation2;
|
||||||
class CGameView;
|
class CGameView;
|
||||||
class CPlayer;
|
|
||||||
class CGameAttributes;
|
|
||||||
class CNetTurnManager;
|
class CNetTurnManager;
|
||||||
|
class CScriptValRooted;
|
||||||
/**
|
struct CColor;
|
||||||
* Default player limit (not counting the Gaia player)
|
|
||||||
* This may be overridden by system.cfg ("max_players")
|
|
||||||
**/
|
|
||||||
#define PS_MAX_PLAYERS 8
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The container that holds the rules, resources and attributes of the game.
|
* 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.
|
* pointer to the CGameView object representing the view into the game world.
|
||||||
**/
|
**/
|
||||||
CGameView *m_GameView;
|
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.
|
* the game has been initialized and ready for use if true.
|
||||||
**/
|
**/
|
||||||
@ -81,6 +63,9 @@ class CGame
|
|||||||
* scale multiplier for simulation rate.
|
* scale multiplier for simulation rate.
|
||||||
**/
|
**/
|
||||||
float m_SimRate;
|
float m_SimRate;
|
||||||
|
|
||||||
|
int m_PlayerID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* enumerated values for game status.
|
* enumerated values for game status.
|
||||||
**/
|
**/
|
||||||
@ -94,9 +79,15 @@ class CGame
|
|||||||
} GameStatus;
|
} GameStatus;
|
||||||
|
|
||||||
CNetTurnManager* m_TurnManager;
|
CNetTurnManager* m_TurnManager;
|
||||||
|
|
||||||
public:
|
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();
|
~CGame();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -104,15 +95,14 @@ public:
|
|||||||
**/
|
**/
|
||||||
bool m_Paused;
|
bool m_Paused;
|
||||||
|
|
||||||
/*
|
void StartGame(const CScriptValRooted& attribs);
|
||||||
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);
|
|
||||||
PSRETURN ReallyStartGame();
|
PSRETURN ReallyStartGame();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify the game of changes in the network connection status.
|
||||||
|
*/
|
||||||
|
void ChangeNetStatus(ENetStatus status);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Perform all per-frame updates
|
Perform all per-frame updates
|
||||||
*/
|
*/
|
||||||
@ -123,41 +113,10 @@ public:
|
|||||||
void UpdateGameStatus();
|
void UpdateGameStatus();
|
||||||
void EndGame();
|
void EndGame();
|
||||||
|
|
||||||
/**
|
int GetPlayerID();
|
||||||
* Get pointer to the local player object.
|
void SetPlayerID(int playerID);
|
||||||
*
|
|
||||||
* @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; }
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
|
|
||||||
/**
|
CColor GetPlayerColour(int player) const;
|
||||||
* 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; }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get m_GameStarted.
|
* Get m_GameStarted.
|
||||||
@ -207,7 +166,7 @@ public:
|
|||||||
* @return float value of m_SimRate.
|
* @return float value of m_SimRate.
|
||||||
**/
|
**/
|
||||||
inline float GetSimRate() const
|
inline float GetSimRate() const
|
||||||
{ return m_SimRate; }
|
{ return m_SimRate; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Replace the current turn manager.
|
* Replace the current turn manager.
|
||||||
@ -216,12 +175,10 @@ public:
|
|||||||
void SetTurnManager(CNetTurnManager* turnManager);
|
void SetTurnManager(CNetTurnManager* turnManager);
|
||||||
|
|
||||||
CNetTurnManager* GetTurnManager() const
|
CNetTurnManager* GetTurnManager() const
|
||||||
{
|
{ return m_TurnManager; }
|
||||||
return m_TurnManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
PSRETURN RegisterInit(CGameAttributes* pAttribs);
|
void RegisterInit(const CScriptValRooted& attribs);
|
||||||
};
|
};
|
||||||
|
|
||||||
extern CGame *g_Game;
|
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/Filesystem.h"
|
||||||
#include "ps/Font.h"
|
#include "ps/Font.h"
|
||||||
#include "ps/Game.h"
|
#include "ps/Game.h"
|
||||||
#include "ps/GameAttributes.h"
|
|
||||||
#include "ps/Globals.h"
|
#include "ps/Globals.h"
|
||||||
#include "ps/Hotkey.h"
|
#include "ps/Hotkey.h"
|
||||||
#include "ps/Loader.h"
|
#include "ps/Loader.h"
|
||||||
@ -71,6 +70,8 @@
|
|||||||
#include "scripting/DOMEvent.h"
|
#include "scripting/DOMEvent.h"
|
||||||
#include "scripting/ScriptableComplex.h"
|
#include "scripting/ScriptableComplex.h"
|
||||||
|
|
||||||
|
#include "scriptinterface/ScriptInterface.h"
|
||||||
|
|
||||||
#include "maths/scripting/JSInterface_Vector3D.h"
|
#include "maths/scripting/JSInterface_Vector3D.h"
|
||||||
|
|
||||||
#include "graphics/scripting/JSInterface_Camera.h"
|
#include "graphics/scripting/JSInterface_Camera.h"
|
||||||
@ -306,21 +307,10 @@ static void RegisterJavascriptInterfaces()
|
|||||||
JSI_Sound::ScriptingInit();
|
JSI_Sound::ScriptingInit();
|
||||||
|
|
||||||
// scripting
|
// scripting
|
||||||
SColour::ScriptingInit();
|
|
||||||
CScriptEvent::ScriptingInit();
|
CScriptEvent::ScriptingInit();
|
||||||
|
|
||||||
// ps
|
// ps
|
||||||
JSI_Console::init();
|
JSI_Console::init();
|
||||||
CGameAttributes::ScriptingInit();
|
|
||||||
CPlayerSlot::ScriptingInit();
|
|
||||||
CPlayer::ScriptingInit();
|
|
||||||
PlayerCollection::Init( "PlayerCollection" );
|
|
||||||
|
|
||||||
// network
|
|
||||||
CNetClient::ScriptingInit();
|
|
||||||
CNetServer::ScriptingInit();
|
|
||||||
CNetSession::ScriptingInit();
|
|
||||||
CServerPlayer::ScriptingInit();
|
|
||||||
|
|
||||||
// GUI
|
// GUI
|
||||||
CGUI::ScriptingInit();
|
CGUI::ScriptingInit();
|
||||||
@ -482,12 +472,13 @@ static void InitInput()
|
|||||||
|
|
||||||
in_add_handler(HotkeyInputHandler);
|
in_add_handler(HotkeyInputHandler);
|
||||||
|
|
||||||
in_add_handler(GlobalsInputHandler);
|
|
||||||
|
|
||||||
// gui_handler needs to be registered after (i.e. called before!) the
|
// gui_handler needs to be registered after (i.e. called before!) the
|
||||||
// hotkey handler so that input boxes can be typed in without
|
// hotkey handler so that input boxes can be typed in without
|
||||||
// setting off hotkeys.
|
// setting off hotkeys.
|
||||||
in_add_handler(gui_handler);
|
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");
|
MICROLOG(L"Shutdown");
|
||||||
|
|
||||||
@ -582,11 +573,6 @@ void Shutdown(int flags)
|
|||||||
|
|
||||||
ShutdownPs(); // Must delete g_GUI before g_ScriptingHost
|
ShutdownPs(); // Must delete g_GUI before g_ScriptingHost
|
||||||
|
|
||||||
if (! (flags & INIT_NO_SIM))
|
|
||||||
{
|
|
||||||
SAFE_DELETE(g_GameAttributes);
|
|
||||||
}
|
|
||||||
|
|
||||||
// destroy actor related stuff
|
// destroy actor related stuff
|
||||||
TIMER_BEGIN(L"shutdown actor stuff");
|
TIMER_BEGIN(L"shutdown actor stuff");
|
||||||
delete &g_MaterialManager;
|
delete &g_MaterialManager;
|
||||||
@ -803,14 +789,6 @@ void Init(const CmdLineArgs& args, int flags)
|
|||||||
ogl_WarnIfError();
|
ogl_WarnIfError();
|
||||||
InitRenderer();
|
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();
|
InitInput();
|
||||||
|
|
||||||
ogl_WarnIfError();
|
ogl_WarnIfError();
|
||||||
@ -833,66 +811,49 @@ void RenderGui(bool RenderingState)
|
|||||||
class AutostartNetServer : public CNetServer
|
class AutostartNetServer : public CNetServer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
AutostartNetServer(CGame *pGame, CGameAttributes *pGameAttributes, int maxPlayers) :
|
AutostartNetServer(const CStr& map, int maxPlayers) :
|
||||||
CNetServer(pGame->GetSimulation2()->GetScriptInterface(), pGame, pGameAttributes), m_NumPlayers(1), m_MaxPlayers(maxPlayers)
|
CNetServer(), m_NeedsStart(false), m_NumPlayers(0), m_MaxPlayers(maxPlayers)
|
||||||
{
|
{
|
||||||
|
CScriptValRooted attrs;
|
||||||
|
GetScriptInterface().Eval("({})", attrs);
|
||||||
|
GetScriptInterface().SetProperty(attrs.get(), "map", std::string(map), false);
|
||||||
|
UpdateGameAttributes(attrs);
|
||||||
}
|
}
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void OnAddPlayer()
|
||||||
|
{
|
||||||
m_NumPlayers++;
|
m_NumPlayers++;
|
||||||
|
|
||||||
debug_printf(L"# player joined (got %d, need %d)\n", (int)m_NumPlayers, (int)m_MaxPlayers);
|
debug_printf(L"# player joined (got %d, need %d)\n", (int)m_NumPlayers, (int)m_MaxPlayers);
|
||||||
|
|
||||||
if (m_NumPlayers >= m_MaxPlayers)
|
if (m_NumPlayers >= m_MaxPlayers)
|
||||||
{
|
m_NeedsStart = true; // delay until next Poll, so the new player has been fully processed
|
||||||
g_GUI->SwitchPage(L"page_loading.xml", JSVAL_VOID);
|
|
||||||
int ret = StartGame();
|
|
||||||
debug_assert(ret == 0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void OnPlayerLeave(CNetSession* UNUSED(pSession))
|
virtual void OnRemovePlayer()
|
||||||
{
|
{
|
||||||
debug_warn(L"client left?!");
|
debug_warn(L"client left?!");
|
||||||
m_NumPlayers--;
|
m_NumPlayers--;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual void Poll()
|
||||||
|
{
|
||||||
|
if (m_NeedsStart)
|
||||||
|
{
|
||||||
|
StartGame();
|
||||||
|
m_NeedsStart = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
CNetServer::Poll();
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
bool m_NeedsStart;
|
||||||
size_t m_NumPlayers;
|
size_t m_NumPlayers;
|
||||||
size_t m_MaxPlayers;
|
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)
|
static bool Autostart(const CmdLineArgs& args)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
@ -908,12 +869,6 @@ static bool Autostart(const CmdLineArgs& args)
|
|||||||
|
|
||||||
g_Game = new CGame();
|
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"))
|
if (args.Has("autostart-host"))
|
||||||
{
|
{
|
||||||
InitPs(true, L"page_loading.xml");
|
InitPs(true, L"page_loading.xml");
|
||||||
@ -922,32 +877,33 @@ static bool Autostart(const CmdLineArgs& args)
|
|||||||
if (args.Has("autostart-players"))
|
if (args.Has("autostart-players"))
|
||||||
maxPlayers = args.Get("autostart-players").ToUInt();
|
maxPlayers = args.Get("autostart-players").ToUInt();
|
||||||
|
|
||||||
g_NetServer = new AutostartNetServer(g_Game, g_GameAttributes, maxPlayers);
|
g_NetServer = new AutostartNetServer(autostartMap, maxPlayers);
|
||||||
// TODO: player name, etc
|
bool ok = g_NetServer->SetupConnection();
|
||||||
bool ok = g_NetServer->Start(NULL, 0, NULL);
|
|
||||||
debug_assert(ok);
|
debug_assert(ok);
|
||||||
|
|
||||||
|
g_NetClient = new CNetClient(g_Game);
|
||||||
|
// TODO: player name, etc
|
||||||
|
g_NetClient->SetupLocalConnection(*g_NetServer);
|
||||||
}
|
}
|
||||||
else if (args.Has("autostart-client"))
|
else if (args.Has("autostart-client"))
|
||||||
{
|
{
|
||||||
InitPs(true, L"page_loading.xml");
|
InitPs(true, L"page_loading.xml");
|
||||||
|
|
||||||
bool ok;
|
g_NetClient = new CNetClient(g_Game);
|
||||||
g_NetClient = new AutostartNetClient(g_Game, g_GameAttributes);
|
|
||||||
// TODO: player name, etc
|
// TODO: player name, etc
|
||||||
ok = g_NetClient->Create();
|
bool ok = g_NetClient->SetupConnection(args.Get("autostart-ip"));
|
||||||
debug_assert(ok);
|
|
||||||
ok = g_NetClient->Connect(args.Get("autostart-ip"), DEFAULT_HOST_PORT);
|
|
||||||
debug_assert(ok);
|
debug_assert(ok);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (int i = 1; i < 8; ++i)
|
CScriptValRooted attrs;
|
||||||
g_GameAttributes->GetSlot(i)->AssignLocal();
|
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);
|
g_Game->SetPlayerID(1);
|
||||||
debug_assert(ret == PSRETURN_OK);
|
g_Game->StartGame(attrs);
|
||||||
LDR_NonprogressiveLoad();
|
LDR_NonprogressiveLoad();
|
||||||
ret = g_Game->ReallyStartGame();
|
PSRETURN ret = g_Game->ReallyStartGame();
|
||||||
debug_assert(ret == PSRETURN_OK);
|
debug_assert(ret == PSRETURN_OK);
|
||||||
|
|
||||||
InitPs(true, L"page_session_new.xml");
|
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.
|
* This file is part of 0 A.D.
|
||||||
*
|
*
|
||||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
* 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.
|
// skip initializing the in-game GUI.
|
||||||
// needed by map editor because it uses its own GUI.
|
// needed by map editor because it uses its own GUI.
|
||||||
INIT_NO_GUI = 2,
|
INIT_NO_GUI = 2
|
||||||
|
|
||||||
// skip initializing the simulation.
|
|
||||||
// used by actor viewer because it doesn't need the simulation code.
|
|
||||||
INIT_NO_SIM = 4
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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/CStr.h"
|
||||||
#include "ps/Errors.h"
|
#include "ps/Errors.h"
|
||||||
#include "ps/Game.h"
|
#include "ps/Game.h"
|
||||||
#include "ps/GameAttributes.h"
|
|
||||||
#include "ps/Loader.h"
|
#include "ps/Loader.h"
|
||||||
#include "ps/LoaderThunks.h"
|
#include "ps/LoaderThunks.h"
|
||||||
#include "ps/World.h"
|
#include "ps/World.h"
|
||||||
@ -68,25 +67,30 @@ CWorld::CWorld(CGame *pGame):
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets up the game world and loads required world resources.
|
* 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
|
// 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;
|
CMapReader* reader = 0;
|
||||||
|
|
||||||
try {
|
try
|
||||||
|
{
|
||||||
reader = new CMapReader;
|
reader = new CMapReader;
|
||||||
CTriggerManager* pTriggerManager = NULL;
|
CTriggerManager* pTriggerManager = NULL;
|
||||||
reader->LoadMap(mapfilename, m_Terrain, m_UnitManager, g_Renderer.GetWaterManager(),
|
reader->LoadMap(mapfilename, m_Terrain, m_UnitManager,
|
||||||
g_Renderer.GetSkyManager(), &g_LightEnv, m_pGame->GetView()->GetCamera(), m_pGame->GetView()->GetCinema(),
|
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);
|
pTriggerManager, m_pGame->GetSimulation2(), NULL);
|
||||||
// fails immediately, or registers for delay loading
|
// fails immediately, or registers for delay loading
|
||||||
} catch (PSERROR_File& err) {
|
}
|
||||||
|
catch (PSERROR_File& err)
|
||||||
|
{
|
||||||
delete reader;
|
delete reader;
|
||||||
LOG(CLogger::Error, LOG_CATEGORY, L"Failed to load map %ls: %hs", mapfilename.string().c_str(), err.what());
|
LOG(CLogger::Error, LOG_CATEGORY, L"Failed to load map %ls: %hs", mapfilename.string().c_str(), err.what());
|
||||||
throw PSERROR_Game_World_MapLoadFailed();
|
throw PSERROR_Game_World_MapLoadFailed();
|
||||||
@ -97,12 +101,10 @@ void CWorld::Initialize(CGameAttributes *pAttribs)
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes the game world with the attributes provided.
|
* 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);
|
ERROR_TYPE(Game_World, MapLoadFailed);
|
||||||
|
|
||||||
class CGame;
|
class CGame;
|
||||||
class CGameAttributes;
|
|
||||||
class CUnitManager;
|
class CUnitManager;
|
||||||
class CEntityManager;
|
class CEntityManager;
|
||||||
class CProjectileManager;
|
class CProjectileManager;
|
||||||
class CLOSManager;
|
class CLOSManager;
|
||||||
class CTerritoryManager;
|
class CTerritoryManager;
|
||||||
class CTerrain;
|
class CTerrain;
|
||||||
|
class CStrW;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CWorld is a general data class containing whatever is needed to accurately represent the world.
|
* CWorld is a general data class containing whatever is needed to accurately represent the world.
|
||||||
@ -76,11 +76,11 @@ public:
|
|||||||
CWorld(CGame *pGame);
|
CWorld(CGame *pGame);
|
||||||
~CWorld();
|
~CWorld();
|
||||||
|
|
||||||
void RegisterInit(CGameAttributes *pGameAttributes);
|
void RegisterInit(const CStrW& mapFile);
|
||||||
/*
|
/*
|
||||||
Initialize the World - load the map and all objects
|
Initialize the World - load the map and all objects
|
||||||
*/
|
*/
|
||||||
void Initialize(CGameAttributes *pGameAttributes);
|
void Initialize(const CStrW& mapFile);
|
||||||
|
|
||||||
// provided for JS _rewritemaps function
|
// provided for JS _rewritemaps function
|
||||||
void RewriteMap();
|
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/Game.h"
|
||||||
#include "ps/Profile.h"
|
#include "ps/Profile.h"
|
||||||
#include "ps/Filesystem.h"
|
#include "ps/Filesystem.h"
|
||||||
#include "ps/Game.h"
|
|
||||||
#include "ps/World.h"
|
#include "ps/World.h"
|
||||||
#include "ps/Player.h"
|
|
||||||
#include "ps/Loader.h"
|
#include "ps/Loader.h"
|
||||||
#include "ps/ProfileViewer.h"
|
#include "ps/ProfileViewer.h"
|
||||||
#include "graphics/Camera.h"
|
#include "graphics/Camera.h"
|
||||||
|
@ -21,27 +21,11 @@
|
|||||||
#include "graphics/ObjectManager.h"
|
#include "graphics/ObjectManager.h"
|
||||||
#include "maths/scripting/JSInterface_Vector3D.h"
|
#include "maths/scripting/JSInterface_Vector3D.h"
|
||||||
#include "ps/Parser.h"
|
#include "ps/Parser.h"
|
||||||
#include "ps/Player.h"
|
|
||||||
#include "lib/sysdep/sysdep.h" // isfinite
|
#include "lib/sysdep/sysdep.h" // isfinite
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <cfloat>
|
#include <cfloat>
|
||||||
#include "scripting/ScriptableComplex.inl"
|
#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
|
// CVector3D
|
||||||
|
|
||||||
template<> CVector3D* ToNative<CVector3D>( JSContext* cx, JSObject* obj )
|
template<> CVector3D* ToNative<CVector3D>( JSContext* cx, JSObject* obj )
|
||||||
|
@ -26,7 +26,6 @@ class CStrW;
|
|||||||
class CScriptObject;
|
class CScriptObject;
|
||||||
class CObjectEntry;
|
class CObjectEntry;
|
||||||
class CVector3D;
|
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<> bool ToPrimitive<CObjectEntry>( JSContext* cx, jsval v, CObjectEntry*& Storage );
|
||||||
template<> jsval ToJSVal<CObjectEntry>( CObjectEntry*& Native );
|
template<> jsval ToJSVal<CObjectEntry>( CObjectEntry*& Native );
|
||||||
|
|
||||||
// CPlayer*
|
|
||||||
template<> bool ToPrimitive<CPlayer*>( JSContext* cx, jsval v, CPlayer*& Storage );
|
|
||||||
template<> JSObject* ToScript<CPlayer*>( CPlayer** Native );
|
|
||||||
|
|
||||||
// CScriptObject
|
// CScriptObject
|
||||||
template<> bool ToPrimitive<CScriptObject>( JSContext* cx, jsval v, CScriptObject& Storage );
|
template<> bool ToPrimitive<CScriptObject>( JSContext* cx, jsval v, CScriptObject& Storage );
|
||||||
template<> jsval ToJSVal<CScriptObject>( CScriptObject& Native );
|
template<> jsval ToJSVal<CScriptObject>( CScriptObject& Native );
|
||||||
|
@ -39,25 +39,23 @@
|
|||||||
#include "lib/svn_revision.h"
|
#include "lib/svn_revision.h"
|
||||||
#include "lib/frequency_filter.h"
|
#include "lib/frequency_filter.h"
|
||||||
#include "maths/scripting/JSInterface_Vector3D.h"
|
#include "maths/scripting/JSInterface_Vector3D.h"
|
||||||
#include "network/NetClient.h"
|
|
||||||
#include "network/NetServer.h"
|
#include "network/NetServer.h"
|
||||||
#include "ps/CConsole.h"
|
#include "ps/CConsole.h"
|
||||||
#include "ps/CLogger.h"
|
#include "ps/CLogger.h"
|
||||||
#include "ps/CStr.h"
|
#include "ps/CStr.h"
|
||||||
#include "ps/Game.h"
|
#include "ps/Game.h"
|
||||||
#include "ps/GameAttributes.h"
|
|
||||||
#include "ps/Globals.h" // g_frequencyFilter
|
#include "ps/Globals.h" // g_frequencyFilter
|
||||||
#include "ps/GameSetup/GameSetup.h"
|
#include "ps/GameSetup/GameSetup.h"
|
||||||
#include "ps/Hotkey.h"
|
#include "ps/Hotkey.h"
|
||||||
#include "ps/ProfileViewer.h"
|
#include "ps/ProfileViewer.h"
|
||||||
#include "ps/World.h"
|
#include "ps/World.h"
|
||||||
#include "ps/i18n.h"
|
#include "ps/i18n.h"
|
||||||
#include "ps/scripting/JSCollection.h"
|
|
||||||
#include "ps/scripting/JSInterface_Console.h"
|
#include "ps/scripting/JSInterface_Console.h"
|
||||||
#include "ps/scripting/JSInterface_VFS.h"
|
#include "ps/scripting/JSInterface_VFS.h"
|
||||||
#include "renderer/Renderer.h"
|
#include "renderer/Renderer.h"
|
||||||
#include "renderer/SkyManager.h"
|
#include "renderer/SkyManager.h"
|
||||||
#include "scriptinterface/ScriptInterface.h"
|
#include "scriptinterface/ScriptInterface.h"
|
||||||
|
#include "simulation/LOSManager.h"
|
||||||
#include "simulation2/Simulation2.h"
|
#include "simulation2/Simulation2.h"
|
||||||
|
|
||||||
#define LOG_CATEGORY L"script"
|
#define LOG_CATEGORY L"script"
|
||||||
@ -194,90 +192,6 @@ JSBool StopJsTimer(JSContext* cx, JSObject*, uintN argc, jsval* argv, jsval* rva
|
|||||||
// Game Setup
|
// 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).
|
// Immediately ends the current game (if any).
|
||||||
// params:
|
// params:
|
||||||
// returns:
|
// returns:
|
||||||
@ -289,14 +203,6 @@ JSBool EndGame(JSContext* cx, JSObject*, uintN argc, jsval* argv, jsval* rval)
|
|||||||
return JS_TRUE;
|
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
|
// 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 )
|
JSBool GetFps( JSContext* cx, JSObject*, uintN argc, jsval* argv, jsval* rval )
|
||||||
{
|
{
|
||||||
JSU_REQUIRE_NO_PARAMS();
|
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;
|
return JS_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -703,11 +612,7 @@ JSFunctionSpec ScriptFunctionTable[] =
|
|||||||
JS_FUNC("stopXTimer", StopJsTimer, 1)
|
JS_FUNC("stopXTimer", StopJsTimer, 1)
|
||||||
|
|
||||||
// Game Setup
|
// Game Setup
|
||||||
JS_FUNC("startGame", StartGame, 0)
|
|
||||||
JS_FUNC("endGame", EndGame, 0)
|
JS_FUNC("endGame", EndGame, 0)
|
||||||
JS_FUNC("getGameMode", GetGameMode, 0)
|
|
||||||
JS_FUNC("createClient", CreateClient, 0)
|
|
||||||
JS_FUNC("createServer", CreateServer, 0)
|
|
||||||
|
|
||||||
// VFS (external)
|
// VFS (external)
|
||||||
JS_FUNC("buildDirEntList", JSI_VFS::BuildDirEntList, 1)
|
JS_FUNC("buildDirEntList", JSI_VFS::BuildDirEntList, 1)
|
||||||
@ -757,45 +662,6 @@ JSFunctionSpec ScriptFunctionTable[] =
|
|||||||
// property accessors
|
// 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 )
|
JSBool GetGameView( JSContext* UNUSED(cx), JSObject* UNUSED(obj), jsval UNUSED(id), jsval* vp )
|
||||||
{
|
{
|
||||||
if (g_Game)
|
if (g_Game)
|
||||||
@ -830,9 +696,6 @@ JSPropertySpec ScriptGlobalTable[] =
|
|||||||
{ "camera" , GLOBAL_CAMERA, JSPROP_PERMANENT, JSI_Camera::getCamera, JSI_Camera::setCamera },
|
{ "camera" , GLOBAL_CAMERA, JSPROP_PERMANENT, JSI_Camera::getCamera, JSI_Camera::setCamera },
|
||||||
{ "console" , GLOBAL_CONSOLE, JSPROP_PERMANENT|JSPROP_READONLY, JSI_Console::getConsole, 0 },
|
{ "console" , GLOBAL_CONSOLE, JSPROP_PERMANENT|JSPROP_READONLY, JSI_Console::getConsole, 0 },
|
||||||
{ "lightenv" , GLOBAL_LIGHTENV, JSPROP_PERMANENT, JSI_LightEnv::getLightEnv, JSI_LightEnv::setLightEnv },
|
{ "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 },
|
{ "gameView" , 0, JSPROP_PERMANENT|JSPROP_READONLY, GetGameView, 0 },
|
||||||
{ "renderer" , 0, JSPROP_PERMANENT|JSPROP_READONLY, GetRenderer, 0 },
|
{ "renderer" , 0, JSPROP_PERMANENT|JSPROP_READONLY, GetRenderer, 0 },
|
||||||
|
|
||||||
|
@ -48,3 +48,9 @@ jsval CScriptValRooted::get() const
|
|||||||
return JSVAL_VOID;
|
return JSVAL_VOID;
|
||||||
return *m_Val;
|
return *m_Val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CScriptValRooted::undefined() const
|
||||||
|
{
|
||||||
|
return (!m_Val || *m_Val == JSVAL_VOID);
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -47,6 +47,8 @@ public:
|
|||||||
|
|
||||||
jsval get() const;
|
jsval get() const;
|
||||||
|
|
||||||
|
bool undefined() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
boost::shared_ptr<jsval> m_Val;
|
boost::shared_ptr<jsval> m_Val;
|
||||||
};
|
};
|
||||||
|
@ -37,6 +37,16 @@ public:
|
|||||||
m_Script.CallVoid("SetName", name);
|
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()
|
virtual CColor GetColour()
|
||||||
{
|
{
|
||||||
return m_Script.Call<CColor>("GetColour");
|
return m_Script.Call<CColor>("GetColour");
|
||||||
|
@ -31,7 +31,8 @@ class ICmpPlayer : public IComponent
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual void SetName(const std::wstring& name) = 0;
|
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;
|
virtual CColor GetColour() = 0;
|
||||||
|
|
||||||
|
@ -38,7 +38,6 @@
|
|||||||
namespace AtlasMessage {
|
namespace AtlasMessage {
|
||||||
|
|
||||||
static bool g_IsInitialised = false;
|
static bool g_IsInitialised = false;
|
||||||
static bool g_DidInitSim;
|
|
||||||
|
|
||||||
MESSAGEHANDLER(Init)
|
MESSAGEHANDLER(Init)
|
||||||
{
|
{
|
||||||
@ -69,12 +68,10 @@ MESSAGEHANDLER(Init)
|
|||||||
g_Quickstart = true;
|
g_Quickstart = true;
|
||||||
|
|
||||||
int flags = INIT_HAVE_VMODE|INIT_NO_GUI;
|
int flags = INIT_HAVE_VMODE|INIT_NO_GUI;
|
||||||
if (! msg->initsimulation)
|
|
||||||
flags |= INIT_NO_SIM;
|
|
||||||
|
|
||||||
Init(g_GameLoop->args, flags);
|
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
|
#if OS_WIN
|
||||||
// HACK (to stop things looking very ugly when scrolling) - should
|
// HACK (to stop things looking very ugly when scrolling) - should
|
||||||
@ -103,8 +100,6 @@ MESSAGEHANDLER(Shutdown)
|
|||||||
g_GameLoop->view = View::GetView_None();
|
g_GameLoop->view = View::GetView_None();
|
||||||
|
|
||||||
int flags = 0;
|
int flags = 0;
|
||||||
if (! g_DidInitSim)
|
|
||||||
flags |= INIT_NO_SIM;
|
|
||||||
Shutdown(flags);
|
Shutdown(flags);
|
||||||
|
|
||||||
g_IsInitialised = false;
|
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.
|
* This file is part of 0 A.D.
|
||||||
*
|
*
|
||||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||||
@ -27,10 +27,10 @@
|
|||||||
#include "graphics/TextureEntry.h"
|
#include "graphics/TextureEntry.h"
|
||||||
#include "graphics/TextureManager.h"
|
#include "graphics/TextureManager.h"
|
||||||
#include "ps/Game.h"
|
#include "ps/Game.h"
|
||||||
#include "ps/GameAttributes.h"
|
|
||||||
#include "ps/Loader.h"
|
#include "ps/Loader.h"
|
||||||
#include "ps/World.h"
|
#include "ps/World.h"
|
||||||
#include "renderer/Renderer.h"
|
#include "renderer/Renderer.h"
|
||||||
|
#include "scriptinterface/ScriptInterface.h"
|
||||||
#include "simulation/LOSManager.h"
|
#include "simulation/LOSManager.h"
|
||||||
#include "simulation/Simulation.h"
|
#include "simulation/Simulation.h"
|
||||||
#include "simulation2/Simulation2.h"
|
#include "simulation2/Simulation2.h"
|
||||||
@ -40,7 +40,7 @@
|
|||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
void InitGame(const CStrW& map)
|
void InitGame()
|
||||||
{
|
{
|
||||||
if (g_Game)
|
if (g_Game)
|
||||||
{
|
{
|
||||||
@ -48,48 +48,23 @@ namespace
|
|||||||
g_Game = NULL;
|
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();
|
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);
|
CStrW mapBase = map.BeforeLast(L".pmp"); // strip the file extension, if any
|
||||||
debug_assert(!cmpPlayerMan.null());
|
|
||||||
|
|
||||||
// TODO: pick a sensible number, give them names and colours etc
|
CScriptValRooted attrs;
|
||||||
size_t numPlayers = 4;
|
g_Game->GetSimulation2()->GetScriptInterface().Eval("({})", attrs);
|
||||||
for (size_t i = 0; i < numPlayers; ++i)
|
g_Game->GetSimulation2()->GetScriptInterface().SetProperty(attrs.get(), "map", std::wstring(mapBase), false);
|
||||||
{
|
|
||||||
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?
|
|
||||||
}
|
|
||||||
|
|
||||||
void StartGame()
|
g_Game->StartGame(attrs);
|
||||||
{
|
|
||||||
PSRETURN ret = g_Game->StartGame(g_GameAttributes);
|
|
||||||
debug_assert(ret == PSRETURN_OK);
|
|
||||||
LDR_NonprogressiveLoad();
|
LDR_NonprogressiveLoad();
|
||||||
ret = g_Game->ReallyStartGame();
|
PSRETURN ret = g_Game->ReallyStartGame();
|
||||||
debug_assert(ret == PSRETURN_OK);
|
debug_assert(ret == PSRETURN_OK);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -98,58 +73,20 @@ namespace AtlasMessage {
|
|||||||
|
|
||||||
MESSAGEHANDLER(GenerateMap)
|
MESSAGEHANDLER(GenerateMap)
|
||||||
{
|
{
|
||||||
InitGame(L"");
|
InitGame();
|
||||||
|
|
||||||
// Convert size in patches to number of vertices
|
// Load the empty default map
|
||||||
int vertices = msg->size * PATCH_SIZE + 1;
|
StartGame(L"_default");
|
||||||
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// TODO: use msg->size somehow
|
||||||
|
// (e.g. load the map then resize the terrain to match it)
|
||||||
|
UNUSED2(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
MESSAGEHANDLER(LoadMap)
|
MESSAGEHANDLER(LoadMap)
|
||||||
{
|
{
|
||||||
InitGame(*msg->filename);
|
InitGame();
|
||||||
StartGame();
|
StartGame(*msg->filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
MESSAGEHANDLER(SaveMap)
|
MESSAGEHANDLER(SaveMap)
|
||||||
|
Loading…
Reference in New Issue
Block a user