mirror of
https://gitea.wildfiregames.com/0ad/0ad
synced 2026-06-16 05:13:58 -07:00
# Add initial basic player AI framework.
Support direct access to serializer streams, so serializers can be nested. Make component script "this.template" read-only. Stop globally-subscribed component scripts receiving messages posted to local components, to reduce out-of-sync risks. Move pathfinder data out of entity template directory. Fix GuiInterface deserialization. This was SVN commit r8865.
This commit is contained in:
parent
1051d10f54
commit
dd501b2a5a
70 changed files with 1681 additions and 267 deletions
|
|
@ -20,9 +20,9 @@ Engine.RegisterComponentType(IID_Test1, "TestScript1_Init", TestScript1_Init);
|
|||
function TestScript1_readonly() {}
|
||||
|
||||
TestScript1_readonly.prototype.GetX = function() {
|
||||
this.template = null;
|
||||
delete this.template;
|
||||
try { this.template.x += 1000; } catch(e) { }
|
||||
try { this.template = null; } catch(e) { }
|
||||
try { delete this.template; } catch(e) { }
|
||||
try { this.template.x += 1000; } catch(e) { }
|
||||
try { delete this.template.x; } catch(e) { }
|
||||
try { this.template.y = 2000; } catch(e) { }
|
||||
return +(this.template.x || 1) + +(this.template.y || 2);
|
||||
|
|
|
|||
43
binaries/data/mods/public/gui/aiconfig/aiconfig.js
Normal file
43
binaries/data/mods/public/gui/aiconfig/aiconfig.js
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
var g_AIs; // [ {"id": ..., "data": {"name": ..., "description": ..., ...} }, ... ]
|
||||
var g_Callback; // for the OK button
|
||||
|
||||
function init(settings)
|
||||
{
|
||||
g_Callback = settings.callback;
|
||||
|
||||
g_AIs = [
|
||||
{id: "", data: {name: "None", description: "AI will be disabled for this player."}}
|
||||
].concat(settings.ais);
|
||||
|
||||
var aiSelection = getGUIObjectByName("aiSelection");
|
||||
aiSelection.list = [ ai.data.name for each (ai in g_AIs) ];
|
||||
|
||||
var selected = 0;
|
||||
for (var i = 0; i < g_AIs.length; ++i)
|
||||
{
|
||||
if (g_AIs[i].id == settings.id)
|
||||
{
|
||||
selected = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
aiSelection.selected = selected;
|
||||
}
|
||||
|
||||
function selectAI(idx)
|
||||
{
|
||||
var id = g_AIs[idx].id;
|
||||
var name = g_AIs[idx].data.name;
|
||||
var description = g_AIs[idx].data.description;
|
||||
|
||||
getGUIObjectByName("aiDescription").caption = description;
|
||||
}
|
||||
|
||||
function returnAI()
|
||||
{
|
||||
var aiSelection = getGUIObjectByName("aiSelection");
|
||||
var idx = aiSelection.selected;
|
||||
var id = g_AIs[idx].id;
|
||||
var name = g_AIs[idx].data.name;
|
||||
g_Callback({"id": id, "name": name});
|
||||
}
|
||||
59
binaries/data/mods/public/gui/aiconfig/aiconfig.xml
Normal file
59
binaries/data/mods/public/gui/aiconfig/aiconfig.xml
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<objects>
|
||||
|
||||
<script file="gui/aiconfig/aiconfig.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%-250 50%-100 50%+250 50%+100">
|
||||
|
||||
<object style="wheatWindowTitleBar" type="text">AI configuration</object>
|
||||
|
||||
<object type="button" style="wheatExit" tooltip_style="snToolTip">
|
||||
<action on="Press"><![CDATA[
|
||||
Engine.PopGuiPage();
|
||||
]]></action>
|
||||
</object>
|
||||
|
||||
<object size="25 10 100%-25 40">
|
||||
<object
|
||||
type="text"
|
||||
font="serif-bold-18"
|
||||
text_align="right"
|
||||
size="0 0 100 100%">
|
||||
AI Player
|
||||
</object>
|
||||
|
||||
<object name="aiSelection" type="dropdown" style="wheatDropDown" size="110 0 310 30">
|
||||
<action on="SelectionChange">selectAI(this.selected);</action>
|
||||
</object>
|
||||
</object>
|
||||
|
||||
<object size="25 50 100%-25 65%-5">
|
||||
|
||||
<object name="aiDescription" type="text" size="0 0 100% 100%"/>
|
||||
|
||||
<!-- TODO: we might want to add things like difficulty controls into here -->
|
||||
|
||||
</object>
|
||||
|
||||
<object type="button" style="wheatButton" size="100%-203 100%-33 100%-103 100%-3">
|
||||
OK
|
||||
<action on="Press"><![CDATA[
|
||||
returnAI();
|
||||
Engine.PopGuiPage();
|
||||
]]></action>
|
||||
</object>
|
||||
|
||||
<object type="button" style="wheatButton" size="100%-103 100%-33 100%-3 100%-3">
|
||||
Cancel
|
||||
<action on="Press"><![CDATA[
|
||||
Engine.PopGuiPage();
|
||||
]]></action>
|
||||
</object>
|
||||
|
||||
</object>
|
||||
|
||||
</objects>
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
// Name displayed for unassigned player slots
|
||||
const NAME_UNASSIGNED = "[color=\"90 90 90 255\"][unassigned]";
|
||||
const NAME_UNASSIGNED = "[color=\"90 90 90 255\"][Computer AI]";
|
||||
|
||||
// Is this is a networked game, or offline
|
||||
var g_IsNetworked;
|
||||
|
|
@ -24,6 +24,7 @@ var g_GameAttributes = {
|
|||
BaseTerrain: "grass1_spring",
|
||||
BaseHeight: 0,
|
||||
PlayerData: [],
|
||||
AIs: [], // indexed by player ID; values are AI id strings
|
||||
RevealMap: false,
|
||||
LockTeams: false,
|
||||
GameType: "conquest"
|
||||
|
|
@ -36,6 +37,8 @@ var g_MaxPlayers = 8;
|
|||
// Number of players for currently selected map
|
||||
var g_NumPlayers = 0;
|
||||
|
||||
var g_AIs = [];
|
||||
|
||||
var g_ChatMessages = [];
|
||||
|
||||
// Data caches
|
||||
|
|
@ -64,7 +67,10 @@ function init(attribs)
|
|||
default:
|
||||
error("Unexpected 'type' in gamesetup init: "+attribs.type);
|
||||
}
|
||||
|
||||
|
||||
// Load AI list
|
||||
g_AIs = Engine.GetAIs();
|
||||
|
||||
// Get default player data - remove gaia
|
||||
var pDefs = initPlayerDefaults();
|
||||
pDefs.shift();
|
||||
|
|
@ -86,7 +92,7 @@ function init(attribs)
|
|||
addFilter("Old Maps", function(settings) { return !settings; });
|
||||
addFilter("All Maps", function(settings) { return true; });
|
||||
|
||||
//Populate map filters dropdown
|
||||
// Populate map filters dropdown
|
||||
var mapFilters = getGUIObjectByName("mapFilterSelection");
|
||||
mapFilters.list = getFilters();
|
||||
g_GameAttributes.mapFilter = "Default";
|
||||
|
|
@ -697,7 +703,7 @@ function onGameAttributesChange()
|
|||
var pTeam = getGUIObjectByName("playerTeam["+i+"]");
|
||||
var pTeamText = getGUIObjectByName("playerTeamText["+i+"]");
|
||||
var pColor = getGUIObjectByName("playerColour["+i+"]");
|
||||
|
||||
|
||||
// Player data / defaults
|
||||
var pData = mapSettings.PlayerData ? mapSettings.PlayerData[i] : {};
|
||||
var pDefs = g_GameAttributes.settings.PlayerData ? g_GameAttributes.settings.PlayerData[i] : {};
|
||||
|
|
@ -705,7 +711,7 @@ function onGameAttributesChange()
|
|||
// Common to all game types
|
||||
var color = iColorToString(getSetting(pData, pDefs, "Colour"));
|
||||
pColor.sprite = "colour:"+color+" 100";
|
||||
pName.caption = getSetting(pData, pDefs, "Name");
|
||||
pName.caption = getSetting(pData, pDefs, "Name");
|
||||
|
||||
var team = getSetting(pData, pDefs, "Team");
|
||||
var civ = getSetting(pData, pDefs, "Civ");
|
||||
|
|
@ -766,7 +772,7 @@ function launchGame()
|
|||
}
|
||||
// Remove extra player data
|
||||
g_GameAttributes.settings.PlayerData = g_GameAttributes.settings.PlayerData.slice(0, g_NumPlayers);
|
||||
|
||||
|
||||
Engine.StartGame(g_GameAttributes, playerID);
|
||||
Engine.SwitchGuiPage("page_loading.xml", {
|
||||
"attribs": g_GameAttributes,
|
||||
|
|
@ -797,13 +803,39 @@ function updatePlayerList()
|
|||
|
||||
for (var i = 0; i < g_MaxPlayers; ++i)
|
||||
{
|
||||
let playerID = i+1;
|
||||
|
||||
var selection = (assignments[playerID] || 0);
|
||||
|
||||
// If no human is assigned, show the AI config button
|
||||
var configButton = getGUIObjectByName("playerConfig["+i+"]");
|
||||
if (selection == 0 && g_IsController)
|
||||
{
|
||||
configButton.hidden = false;
|
||||
configButton.onpress = function()
|
||||
{
|
||||
Engine.PushGuiPage("page_aiconfig.xml", {
|
||||
ais: g_AIs,
|
||||
id: g_GameAttributes.settings.AIs[playerID],
|
||||
callback: function(ai) {
|
||||
if (ai.id == "")
|
||||
delete g_GameAttributes.settings.AIs[playerID];
|
||||
else
|
||||
g_GameAttributes.settings.AIs[playerID] = ai.id;
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
configButton.hidden = true;
|
||||
}
|
||||
|
||||
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 ()
|
||||
|
|
|
|||
|
|
@ -103,8 +103,12 @@
|
|||
<object name="playerColour[n]" type="image" size="0 0 100% 100%"/>
|
||||
<object name="playerName[n]" type="text" text_align="right" text_valign="center" size="0 0 100 100%"/>
|
||||
<object name="playerAssignment[n]" type="dropdown" style="wheatDropDown" size="100 2 250 100%-2" tooltip_style="onscreenToolTip" tooltip="Select player"/>
|
||||
<object name="playerConfig[n]" type="button" style="wheatButton" size="251 6 264 24"
|
||||
tooltip_style="onscreenToolTip"
|
||||
tooltip="Configure AI settings"
|
||||
>c</object>
|
||||
<object name="playerCiv[n]" type="dropdown" style="wheatDropDown" size="265 2 395 100%-2" tooltip_style="onscreenToolTip" tooltip="Select player's civilization"/>
|
||||
<object name="playerCivText[n]" type="text" text_align="center" text_valign="center" size="250 0 400 100%"/>
|
||||
<object name="playerCivText[n]" type="text" text_align="center" text_valign="center" size="265 0 395 100%"/>
|
||||
<object name="playerTeam[n]" type="dropdown" style="wheatDropDown" size="415 2 485 100%-2" tooltip_style="onscreenToolTip" tooltip="Select player's team"/>
|
||||
<object name="playerTeamText[n]" type="text" text_align="center" text_valign="center" size="415 0 485 100%"/>
|
||||
</object>
|
||||
|
|
@ -188,4 +192,4 @@
|
|||
|
||||
</object>
|
||||
|
||||
</objects>
|
||||
</objects>
|
||||
|
|
|
|||
7
binaries/data/mods/public/gui/page_aiconfig.xml
Normal file
7
binaries/data/mods/public/gui/page_aiconfig.xml
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<page>
|
||||
<include>common/setup.xml</include>
|
||||
<include>common/sprite1.xml</include>
|
||||
<include>common/styles.xml</include>
|
||||
<include>aiconfig/aiconfig.xml</include>
|
||||
</page>
|
||||
|
|
@ -119,6 +119,17 @@ function getPlayerData(playerAssignments)
|
|||
return players;
|
||||
}
|
||||
|
||||
function findGuidForPlayerID(playerAssignments, player)
|
||||
{
|
||||
for (var playerGuid in playerAssignments)
|
||||
{
|
||||
var playerAssignment = playerAssignments[playerGuid];
|
||||
if (playerAssignment.player == player)
|
||||
return playerGuid;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Update player data when a host has connected
|
||||
function updatePlayerDataAdd(players, hostGuid, playerAssignment)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:f1af6b6dde1e44ef7126355bfe25a3e4ed4cdf678a12351d8d1bdf89b9e86314
|
||||
size 2860
|
||||
oid sha256:cc6a53ac292f004227ee7a7a1bfacdfe5d7f03fac42f750e4e3f4406b420751c
|
||||
size 2712
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:4b5bdfec10c985668216314a92a94a565cf1a96fa3df22657d57c4e64cc7ddc9
|
||||
size 2387
|
||||
oid sha256:057a66b494b3f695fb6a296cf15a499ee2d4343f8623457546e53ce627d962ca
|
||||
size 2391
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"name": "Scaredy Bot",
|
||||
"description": "An AI that is terrified by the mere possibility of having to fight.",
|
||||
"constructor": "ScaredyBotAI"
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
function ScaredyBotAI(settings)
|
||||
{
|
||||
warn("Constructing ScaredyBotAI for player "+settings.player);
|
||||
|
||||
this.player = settings.player;
|
||||
this.turn = 0;
|
||||
this.suicideTurn = 20;
|
||||
}
|
||||
|
||||
ScaredyBotAI.prototype.HandleMessage = function(state)
|
||||
{
|
||||
// print("### HandleMessage("+uneval(state)+")\n\n");
|
||||
// print(uneval(this)+"\n\n");
|
||||
|
||||
if (this.turn == 0)
|
||||
{
|
||||
Engine.PostCommand({"type": "chat", "message": "Good morning."});
|
||||
}
|
||||
|
||||
if (this.turn == this.suicideTurn)
|
||||
{
|
||||
Engine.PostCommand({"type": "chat", "message": "I quake in my boots! My troops cannot hope to survive against a power such as yours."});
|
||||
|
||||
var myEntities = [];
|
||||
for (var ent in state.entities)
|
||||
if (state.entities[ent].player == this.player)
|
||||
myEntities.push(+ent);
|
||||
Engine.PostCommand({"type": "delete-entities", "entities": myEntities});
|
||||
}
|
||||
|
||||
this.turn++;
|
||||
};
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
function AIInterface() {}
|
||||
|
||||
AIInterface.prototype.Schema =
|
||||
"<a:component type='system'/><empty/>";
|
||||
|
||||
AIInterface.prototype.Init = function()
|
||||
{
|
||||
this.events = [];
|
||||
};
|
||||
|
||||
AIInterface.prototype.GetRepresentation = function()
|
||||
{
|
||||
var cmpGuiInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
|
||||
|
||||
// Return the same game state as the GUI uses
|
||||
var state = cmpGuiInterface.GetSimulationState(-1);
|
||||
|
||||
// Add some extra AI-specific data
|
||||
state.events = this.events;
|
||||
|
||||
// Reset the event list for the next turn
|
||||
this.events = [];
|
||||
|
||||
return state;
|
||||
};
|
||||
|
||||
// Set up a load of event handlers to capture interesting things going on
|
||||
// in the world, which we will report to AI:
|
||||
// (This shouldn't include extremely high-frequency events, like PositionChanged,
|
||||
// because that would be very expensive and AI will rarely care about all those
|
||||
// events.)
|
||||
|
||||
AIInterface.prototype.OnGlobalCreate = function(msg)
|
||||
{
|
||||
this.events.push({"type": "Create", "msg": msg});
|
||||
};
|
||||
|
||||
AIInterface.prototype.OnGlobalDestroy = function(msg)
|
||||
{
|
||||
this.events.push({"type": "Destroy", "msg": msg});
|
||||
};
|
||||
|
||||
AIInterface.prototype.OnGlobalOwnershipChanged = function(msg)
|
||||
{
|
||||
this.events.push({"type": "OwnershipChanged", "msg": msg});
|
||||
};
|
||||
|
||||
AIInterface.prototype.OnGlobalAttacked = function(msg)
|
||||
{
|
||||
this.events.push({"type": "Attacked", "msg": msg});
|
||||
};
|
||||
|
||||
AIInterface.prototype.OnGlobalConstructionFinished = function(msg)
|
||||
{
|
||||
this.events.push({"type": "ConstructionFinished", "msg": msg});
|
||||
};
|
||||
|
||||
AIInterface.prototype.OnGlobalPlayerDefeated = function(msg)
|
||||
{
|
||||
this.events.push({"type": "PlayerDefeated", "msg": msg});
|
||||
};
|
||||
|
||||
Engine.RegisterComponentType(IID_AIInterface, "AIInterface", AIInterface);
|
||||
20
binaries/data/mods/public/simulation/components/AIProxy.js
Normal file
20
binaries/data/mods/public/simulation/components/AIProxy.js
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
function AIProxy() {}
|
||||
|
||||
AIProxy.prototype.Schema =
|
||||
"<empty/>";
|
||||
|
||||
AIProxy.prototype.Init = function()
|
||||
{
|
||||
};
|
||||
|
||||
AIProxy.prototype.GetRepresentation = function()
|
||||
{
|
||||
// Currently we'll just return the same data that the GUI uses.
|
||||
// Maybe we should add/remove things (or make it more efficient)
|
||||
// later.
|
||||
|
||||
var cmpGuiInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
|
||||
return cmpGuiInterface.GetEntityState(-1, this.entity);
|
||||
};
|
||||
|
||||
Engine.RegisterComponentType(IID_AIProxy, "AIProxy", AIProxy);
|
||||
|
|
@ -5,9 +5,16 @@ GuiInterface.prototype.Schema =
|
|||
|
||||
GuiInterface.prototype.Serialize = function()
|
||||
{
|
||||
// This component isn't network-synchronised so we mustn't serialise
|
||||
// its non-deterministic data. Instead just return an empty object.
|
||||
return {};
|
||||
};
|
||||
|
||||
GuiInterface.prototype.Deserialize = function(obj)
|
||||
{
|
||||
this.Init();
|
||||
};
|
||||
|
||||
GuiInterface.prototype.Init = function()
|
||||
{
|
||||
this.placementEntity = undefined; // = undefined or [templateName, entityID]
|
||||
|
|
@ -15,10 +22,15 @@ GuiInterface.prototype.Init = function()
|
|||
this.notifications = [];
|
||||
};
|
||||
|
||||
/**
|
||||
* All of the functions defined below are called via Engine.GuiInterfaceCall(name, arg) from GUI scripts, and executed here with arguments (player, arg)
|
||||
*/
|
||||
/*
|
||||
* All of the functions defined below are called via Engine.GuiInterfaceCall(name, arg)
|
||||
* from GUI scripts, and executed here with arguments (player, arg).
|
||||
*/
|
||||
|
||||
/**
|
||||
* Returns global information about the current game state.
|
||||
* This is used by the GUI and also by AI scripts.
|
||||
*/
|
||||
GuiInterface.prototype.GetSimulationState = function(player)
|
||||
{
|
||||
var ret = {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,14 @@
|
|||
function InitGame(settings)
|
||||
{
|
||||
// This will be called after the map (settings) have been loaded, before the simulation has started.
|
||||
// TODO: Is this even needed?
|
||||
if (!settings)
|
||||
settings = {};
|
||||
// This will be called after the map settings have been loaded,
|
||||
// before the simulation has started.
|
||||
|
||||
var cmpAIManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_AIManager);
|
||||
for (var i = 0; i < settings.AIs.length; ++i)
|
||||
{
|
||||
if (settings.AIs[i])
|
||||
cmpAIManager.AddPlayer(settings.AIs[i], i);
|
||||
}
|
||||
}
|
||||
|
||||
Engine.RegisterGlobal("InitGame", InitGame);
|
||||
|
|
|
|||
|
|
@ -9,12 +9,12 @@ function LoadPlayerSettings(settings)
|
|||
// Default settings
|
||||
if (!settings)
|
||||
settings = {};
|
||||
|
||||
|
||||
// Get default player data
|
||||
var rawData = Engine.ReadJSONFile("player_defaults.json");
|
||||
if (!(rawData && rawData.PlayerData))
|
||||
error("Error reading player default data: player_defaults.json");
|
||||
|
||||
|
||||
var playerDefaults = rawData.PlayerData;
|
||||
|
||||
// default number of players
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ function LoadMapSettings(settings)
|
|||
if (!settings)
|
||||
settings = {};
|
||||
|
||||
|
||||
if (settings.DefaultStance)
|
||||
{
|
||||
for each (var ent in Engine.GetEntitiesWithInterface(IID_UnitAI))
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Entity>
|
||||
<Position>
|
||||
<Altitude>0</Altitude>
|
||||
<Anchor>upright</Anchor>
|
||||
<Floating>false</Floating>
|
||||
</Position>
|
||||
<Ownership/>
|
||||
<Selectable/>
|
||||
<AIProxy/>
|
||||
<Ownership/>
|
||||
<Position>
|
||||
<Altitude>0</Altitude>
|
||||
<Anchor>upright</Anchor>
|
||||
<Floating>false</Floating>
|
||||
</Position>
|
||||
<Selectable/>
|
||||
</Entity>
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Entity>
|
||||
<Position>
|
||||
<Altitude>0</Altitude>
|
||||
<Anchor>upright</Anchor>
|
||||
<Floating>false</Floating>
|
||||
</Position>
|
||||
<Ownership/>
|
||||
<AIProxy/>
|
||||
<Ownership/>
|
||||
<Position>
|
||||
<Altitude>0</Altitude>
|
||||
<Anchor>upright</Anchor>
|
||||
<Floating>false</Floating>
|
||||
</Position>
|
||||
</Entity>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (C) 2010 Wildfire Games.
|
||||
/* Copyright (C) 2011 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
|
|
@ -40,6 +40,7 @@
|
|||
#include "ps/GameSetup/Config.h"
|
||||
|
||||
#include "simulation2/Simulation2.h"
|
||||
#include "simulation2/components/ICmpAIManager.h"
|
||||
#include "simulation2/components/ICmpCommandQueue.h"
|
||||
#include "simulation2/components/ICmpGuiInterface.h"
|
||||
#include "simulation2/components/ICmpRangeManager.h"
|
||||
|
|
@ -280,6 +281,13 @@ void SendNetworkChat(void* UNUSED(cbdata), std::wstring message)
|
|||
g_NetClient->SendChatMessage(message);
|
||||
}
|
||||
|
||||
std::vector<CScriptValRooted> GetAIs(void* cbdata)
|
||||
{
|
||||
CGUIManager* guiManager = static_cast<CGUIManager*> (cbdata);
|
||||
|
||||
return ICmpAIManager::GetAIs(guiManager->GetScriptInterface());
|
||||
}
|
||||
|
||||
void OpenURL(void* UNUSED(cbdata), std::string url)
|
||||
{
|
||||
sys_open_url(url);
|
||||
|
|
@ -426,6 +434,7 @@ void GuiScriptingInit(ScriptInterface& scriptInterface)
|
|||
scriptInterface.RegisterFunction<void, CScriptVal, &SetNetworkGameAttributes>("SetNetworkGameAttributes");
|
||||
scriptInterface.RegisterFunction<void, int, std::string, &AssignNetworkPlayer>("AssignNetworkPlayer");
|
||||
scriptInterface.RegisterFunction<void, std::wstring, &SendNetworkChat>("SendNetworkChat");
|
||||
scriptInterface.RegisterFunction<std::vector<CScriptValRooted>, &GetAIs>("GetAIs");
|
||||
|
||||
// Misc functions
|
||||
scriptInterface.RegisterFunction<std::wstring, std::wstring, &SetCursor>("SetCursor");
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (C) 2010 Wildfire Games.
|
||||
/* Copyright (C) 2011 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
|
|
@ -15,12 +15,6 @@
|
|||
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* File : Game.cpp
|
||||
* Project : engine
|
||||
* Description : Contains the CGame Class implementation.
|
||||
*
|
||||
**/
|
||||
#include "precompiled.h"
|
||||
|
||||
#include "Game.h"
|
||||
|
|
@ -78,11 +72,6 @@ CGame::CGame(bool disableGraphics):
|
|||
m_TurnManager = new CNetLocalTurnManager(*m_Simulation2, GetReplayLogger()); // this will get replaced if we're a net server/client
|
||||
|
||||
m_Simulation2->LoadDefaultScripts();
|
||||
m_Simulation2->ResetState();
|
||||
|
||||
// TODO: If this function is even needed now, get initData
|
||||
CScriptVal initData;
|
||||
m_Simulation2->InitGame(initData);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -91,6 +80,9 @@ CGame::CGame(bool disableGraphics):
|
|||
**/
|
||||
CGame::~CGame()
|
||||
{
|
||||
// Clear rooted value before destroying its context
|
||||
m_RegisteredAttribs = CScriptValRooted();
|
||||
|
||||
// Again, the in-game call tree is going to be different to the main menu one.
|
||||
if (CProfileManager::IsInitialised())
|
||||
g_Profiler.StructuralReset();
|
||||
|
|
@ -121,6 +113,8 @@ void CGame::SetTurnManager(CNetTurnManager* turnManager)
|
|||
**/
|
||||
void CGame::RegisterInit(const CScriptValRooted& attribs)
|
||||
{
|
||||
m_RegisteredAttribs = attribs; // save the attributes for ReallyStartGame
|
||||
|
||||
std::string mapType;
|
||||
m_Simulation2->GetScriptInterface().GetProperty(attribs.get(), "mapType", mapType);
|
||||
|
||||
|
|
@ -147,7 +141,6 @@ void CGame::RegisterInit(const CScriptValRooted& attribs)
|
|||
// TODO: Coming in another patch
|
||||
}
|
||||
|
||||
|
||||
LDR_EndRegistering();
|
||||
}
|
||||
|
||||
|
|
@ -158,6 +151,10 @@ void CGame::RegisterInit(const CScriptValRooted& attribs)
|
|||
**/
|
||||
PSRETURN CGame::ReallyStartGame()
|
||||
{
|
||||
CScriptVal settings;
|
||||
m_Simulation2->GetScriptInterface().GetProperty(m_RegisteredAttribs.get(), "settings", settings);
|
||||
m_Simulation2->InitGame(settings);
|
||||
|
||||
// Call the reallyStartGame GUI function, but only if it exists
|
||||
if (g_GUI && g_GUI->HasPages())
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (C) 2010 Wildfire Games.
|
||||
/* Copyright (C) 2011 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
|
|
@ -15,23 +15,18 @@
|
|||
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* File : Game.h
|
||||
* Project : engine
|
||||
* Description : Contains the CGame Class which is a representation of the game itself.
|
||||
*
|
||||
**/
|
||||
#ifndef INCLUDED_GAME
|
||||
#define INCLUDED_GAME
|
||||
|
||||
#include "ps/Errors.h"
|
||||
#include <vector>
|
||||
|
||||
#include "scriptinterface/ScriptVal.h"
|
||||
|
||||
class CWorld;
|
||||
class CSimulation2;
|
||||
class CGameView;
|
||||
class CNetTurnManager;
|
||||
class CScriptValRooted;
|
||||
class IReplayLogger;
|
||||
struct CColor;
|
||||
|
||||
|
|
@ -159,6 +154,7 @@ public:
|
|||
private:
|
||||
void RegisterInit(const CScriptValRooted& attribs);
|
||||
IReplayLogger* m_ReplayLogger;
|
||||
CScriptValRooted m_RegisteredAttribs;
|
||||
};
|
||||
|
||||
extern CGame *g_Game;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (C) 2010 Wildfire Games.
|
||||
/* Copyright (C) 2011 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
|
|
@ -915,6 +915,32 @@ static bool Autostart(const CmdLineArgs& args)
|
|||
scriptInterface.SetProperty(attrs.get(), "mapType", std::string("scenario"), false);
|
||||
scriptInterface.SetProperty(attrs.get(), "map", std::string(autostartMap), false);
|
||||
|
||||
// Set attrs.settings = { AIs: [...] }:
|
||||
|
||||
/*
|
||||
* Handle command-line options for AI:
|
||||
* -autostart-ai=1:dummybot -autostart-ai=2:dummybot -- adds the dummybot AI to players 1 and 2
|
||||
*/
|
||||
CScriptValRooted ais;
|
||||
scriptInterface.Eval("([])", ais);
|
||||
if (args.Has("autostart-ai"))
|
||||
{
|
||||
std::vector<CStr> aiArgs = args.GetMultiple("autostart-ai");
|
||||
for (size_t i = 0; i < aiArgs.size(); ++i)
|
||||
{
|
||||
int player = aiArgs[i].BeforeFirst(":").ToInt();
|
||||
CStr name = aiArgs[i].AfterFirst(":");
|
||||
scriptInterface.SetPropertyInt(ais.get(), player, std::string(name), false);
|
||||
}
|
||||
}
|
||||
|
||||
CScriptValRooted settings;
|
||||
scriptInterface.Eval("({})", settings);
|
||||
scriptInterface.SetProperty(settings.get(), "AIs", ais, false);
|
||||
scriptInterface.SetProperty(attrs.get(), "settings", settings, false);
|
||||
|
||||
|
||||
|
||||
CScriptValRooted mpInitData;
|
||||
g_GUI->GetScriptInterface().Eval("({isNetworked:true, playerAssignments:{}})", mpInitData);
|
||||
g_GUI->GetScriptInterface().SetProperty(mpInitData.get(), "attribs",
|
||||
|
|
|
|||
|
|
@ -244,27 +244,20 @@ template<typename T> static bool FromJSVal_vector(JSContext* cx, jsval v, std::v
|
|||
return true;
|
||||
}
|
||||
|
||||
template<> jsval ScriptInterface::ToJSVal<std::vector<int> >(JSContext* cx, const std::vector<int>& val)
|
||||
{
|
||||
return ToJSVal_vector(cx, val);
|
||||
}
|
||||
// Instantiate various vector types:
|
||||
|
||||
template<> jsval ScriptInterface::ToJSVal<std::vector<u32> >(JSContext* cx, const std::vector<u32>& val)
|
||||
{
|
||||
return ToJSVal_vector(cx, val);
|
||||
}
|
||||
#define VECTOR(T) \
|
||||
template<> jsval ScriptInterface::ToJSVal<std::vector<T> >(JSContext* cx, const std::vector<T>& val) \
|
||||
{ \
|
||||
return ToJSVal_vector(cx, val); \
|
||||
} \
|
||||
template<> bool ScriptInterface::FromJSVal<std::vector<T> >(JSContext* cx, jsval v, std::vector<T>& out) \
|
||||
{ \
|
||||
return FromJSVal_vector(cx, v, out); \
|
||||
}
|
||||
|
||||
template<> jsval ScriptInterface::ToJSVal<std::vector<std::wstring> >(JSContext* cx, const std::vector<std::wstring>& val)
|
||||
{
|
||||
return ToJSVal_vector(cx, val);
|
||||
}
|
||||
|
||||
template<> bool ScriptInterface::FromJSVal<std::vector<int> >(JSContext* cx, jsval v, std::vector<int>& out)
|
||||
{
|
||||
return FromJSVal_vector(cx, v, out);
|
||||
}
|
||||
|
||||
template<> bool ScriptInterface::FromJSVal<std::vector<u32> >(JSContext* cx, jsval v, std::vector<u32>& out)
|
||||
{
|
||||
return FromJSVal_vector(cx, v, out);
|
||||
}
|
||||
VECTOR(int)
|
||||
VECTOR(u32)
|
||||
VECTOR(std::string)
|
||||
VECTOR(std::wstring)
|
||||
VECTOR(CScriptValRooted)
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
#include "lib/debug.h"
|
||||
#include "lib/utf8.h"
|
||||
#include "ps/CLogger.h"
|
||||
#include "ps/Filesystem.h"
|
||||
#include "ps/Profile.h"
|
||||
#include "ps/utf16string.h"
|
||||
|
||||
|
|
@ -402,7 +403,7 @@ AutoGCRooter* ScriptInterface::ReplaceAutoGCRooter(AutoGCRooter* rooter)
|
|||
}
|
||||
|
||||
|
||||
jsval ScriptInterface::CallConstructor(jsval ctor)
|
||||
jsval ScriptInterface::CallConstructor(jsval ctor, jsval arg)
|
||||
{
|
||||
if (!JSVAL_IS_OBJECT(ctor))
|
||||
{
|
||||
|
|
@ -410,7 +411,43 @@ jsval ScriptInterface::CallConstructor(jsval ctor)
|
|||
return JSVAL_VOID;
|
||||
}
|
||||
|
||||
return OBJECT_TO_JSVAL(JS_New(m->m_cx, JSVAL_TO_OBJECT(ctor), 0, NULL));
|
||||
return OBJECT_TO_JSVAL(JS_New(m->m_cx, JSVAL_TO_OBJECT(ctor), 1, &arg));
|
||||
}
|
||||
|
||||
jsval ScriptInterface::NewObjectFromConstructor(jsval ctor)
|
||||
{
|
||||
// Get the constructor's prototype
|
||||
// (Can't use JS_GetPrototype, since we want .prototype not .__proto__)
|
||||
jsval protoVal;
|
||||
if (!JS_GetProperty(m->m_cx, JSVAL_TO_OBJECT(ctor), "prototype", &protoVal))
|
||||
{
|
||||
LOGERROR(L"NewObjectFromConstructor: can't get prototype");
|
||||
return JSVAL_VOID;
|
||||
}
|
||||
|
||||
if (!JSVAL_IS_OBJECT(protoVal))
|
||||
{
|
||||
LOGERROR(L"NewObjectFromConstructor: prototype is not an object");
|
||||
return JSVAL_VOID;
|
||||
}
|
||||
|
||||
JSObject* proto = JSVAL_TO_OBJECT(protoVal);
|
||||
JSObject* parent = JS_GetParent(m->m_cx, JSVAL_TO_OBJECT(ctor));
|
||||
// TODO: rooting?
|
||||
if (!proto || !parent)
|
||||
{
|
||||
LOGERROR(L"NewObjectFromConstructor: null proto/parent");
|
||||
return JSVAL_VOID;
|
||||
}
|
||||
|
||||
JSObject* obj = JS_NewObject(m->m_cx, NULL, proto, parent);
|
||||
if (!obj)
|
||||
{
|
||||
LOGERROR(L"NewObjectFromConstructor: object creation failed");
|
||||
return JSVAL_VOID;
|
||||
}
|
||||
|
||||
return OBJECT_TO_JSVAL(obj);
|
||||
}
|
||||
|
||||
bool ScriptInterface::CallFunctionVoid(jsval val, const char* name)
|
||||
|
|
@ -465,13 +502,13 @@ bool ScriptInterface::SetGlobal_(const char* name, jsval value, bool replace)
|
|||
return ok ? true : false;
|
||||
}
|
||||
|
||||
bool ScriptInterface::SetProperty_(jsval obj, const char* name, jsval value, bool constant)
|
||||
bool ScriptInterface::SetProperty_(jsval obj, const char* name, jsval value, bool constant, bool enumerate)
|
||||
{
|
||||
uintN attrs;
|
||||
uintN attrs = 0;
|
||||
if (constant)
|
||||
attrs = JSPROP_READONLY | JSPROP_PERMANENT;
|
||||
else
|
||||
attrs = JSPROP_ENUMERATE;
|
||||
attrs |= JSPROP_READONLY | JSPROP_PERMANENT;
|
||||
if (enumerate)
|
||||
attrs |= JSPROP_ENUMERATE;
|
||||
|
||||
if (! JSVAL_IS_OBJECT(obj))
|
||||
return false;
|
||||
|
|
@ -482,6 +519,23 @@ bool ScriptInterface::SetProperty_(jsval obj, const char* name, jsval value, boo
|
|||
return true;
|
||||
}
|
||||
|
||||
bool ScriptInterface::SetPropertyInt_(jsval obj, int name, jsval value, bool constant, bool enumerate)
|
||||
{
|
||||
uintN attrs = 0;
|
||||
if (constant)
|
||||
attrs |= JSPROP_READONLY | JSPROP_PERMANENT;
|
||||
if (enumerate)
|
||||
attrs |= JSPROP_ENUMERATE;
|
||||
|
||||
if (! JSVAL_IS_OBJECT(obj))
|
||||
return false;
|
||||
JSObject* object = JSVAL_TO_OBJECT(obj);
|
||||
|
||||
if (! JS_DefinePropertyById(m->m_cx, object, INT_TO_JSID(name), value, NULL, NULL, attrs))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ScriptInterface::GetProperty_(jsval obj, const char* name, jsval& out)
|
||||
{
|
||||
if (! JSVAL_IS_OBJECT(obj))
|
||||
|
|
@ -552,6 +606,17 @@ bool ScriptInterface::SetPrototype(jsval obj, jsval proto)
|
|||
return JS_SetPrototype(m->m_cx, JSVAL_TO_OBJECT(obj), JSVAL_TO_OBJECT(proto)) ? true : false;
|
||||
}
|
||||
|
||||
bool ScriptInterface::FreezeObject(jsval obj, bool deep)
|
||||
{
|
||||
if (!JSVAL_IS_OBJECT(obj))
|
||||
return false;
|
||||
|
||||
if (deep)
|
||||
return JS_DeepFreezeObject(m->m_cx, JSVAL_TO_OBJECT(obj));
|
||||
else
|
||||
return JS_FreezeObject(m->m_cx, JSVAL_TO_OBJECT(obj));
|
||||
}
|
||||
|
||||
bool ScriptInterface::LoadScript(const std::wstring& filename, const std::wstring& code)
|
||||
{
|
||||
std::string fnAscii(filename.begin(), filename.end());
|
||||
|
|
@ -572,6 +637,41 @@ bool ScriptInterface::LoadScript(const std::wstring& filename, const std::wstrin
|
|||
return ok ? true : false;
|
||||
}
|
||||
|
||||
bool ScriptInterface::LoadGlobalScriptFile(const VfsPath& path)
|
||||
{
|
||||
if (!FileExists(g_VFS, path))
|
||||
{
|
||||
LOGERROR(L"File '%ls' does not exist", path.string().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
CVFSFile file;
|
||||
|
||||
PSRETURN ret = file.Load(g_VFS, path);
|
||||
|
||||
if (ret != PSRETURN_OK)
|
||||
{
|
||||
LOGERROR(L"Failed to load file '%ls': %hs", path.string().c_str(), GetErrorString(ret));
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string content(file.GetBuffer(), file.GetBuffer() + file.GetBufferSize());
|
||||
std::wstring code = wstring_from_utf8(content);
|
||||
|
||||
std::string fnAscii(path.string().begin(), path.string().end());
|
||||
|
||||
// Compile the code in strict mode, to encourage better coding practices and
|
||||
// to possibly help SpiderMonkey with optimisations
|
||||
std::wstring codeStrict = L"\"use strict\";\n" + code;
|
||||
utf16string codeUtf16(codeStrict.begin(), codeStrict.end());
|
||||
uintN lineNo = 0; // put the automatic 'use strict' on line 0, so the real code starts at line 1
|
||||
|
||||
jsval rval;
|
||||
JSBool ok = JS_EvaluateUCScript(m->m_cx, m->m_glob, reinterpret_cast<const jschar*> (codeUtf16.c_str()), (uintN)(codeUtf16.length()), fnAscii.c_str(), lineNo, &rval);
|
||||
return ok ? true : false;
|
||||
}
|
||||
|
||||
|
||||
bool ScriptInterface::Eval(const char* code)
|
||||
{
|
||||
jsval rval;
|
||||
|
|
@ -635,6 +735,29 @@ CScriptValRooted ScriptInterface::ParseJSON(const std::string& string_utf8)
|
|||
return ParseJSON(utf16string(attrsW.begin(), attrsW.end()));
|
||||
}
|
||||
|
||||
CScriptValRooted ScriptInterface::ReadJSONFile(const VfsPath& path)
|
||||
{
|
||||
if (!FileExists(g_VFS, path))
|
||||
{
|
||||
LOGERROR(L"File '%ls' does not exist", path.string().c_str());
|
||||
return CScriptValRooted();
|
||||
}
|
||||
|
||||
CVFSFile file;
|
||||
|
||||
PSRETURN ret = file.Load(g_VFS, path);
|
||||
|
||||
if (ret != PSRETURN_OK)
|
||||
{
|
||||
LOGERROR(L"Failed to load file '%ls': %hs", path.string().c_str(), GetErrorString(ret));
|
||||
return CScriptValRooted();
|
||||
}
|
||||
|
||||
std::string content(file.GetBuffer(), file.GetBuffer() + file.GetBufferSize()); // assume it's UTF-8
|
||||
|
||||
return ParseJSON(content);
|
||||
}
|
||||
|
||||
struct Stringifier
|
||||
{
|
||||
static JSBool callback(const jschar* buf, uint32 len, void* data)
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
|
||||
#include "js/jsapi.h"
|
||||
|
||||
#include "lib/file/vfs/vfs_path.h"
|
||||
#include "ps/Profile.h"
|
||||
#include "ps/utf16string.h"
|
||||
|
||||
|
|
@ -85,11 +86,17 @@ public:
|
|||
void ReplaceNondeterministicFunctions(boost::rand48& rng);
|
||||
|
||||
/**
|
||||
* Call a constructor function, roughly equivalent to JS "new ctor".
|
||||
*
|
||||
* @return The new object; or 0 on failure, and logs an error message
|
||||
* Call a constructor function, equivalent to JS "new ctor(arg)".
|
||||
* @return The new object; or JSVAL_VOID on failure, and logs an error message
|
||||
*/
|
||||
jsval CallConstructor(jsval ctor);
|
||||
jsval CallConstructor(jsval ctor, jsval arg);
|
||||
|
||||
/**
|
||||
* Create an object as with CallConstructor except don't actually execute the
|
||||
* constructor function.
|
||||
* @return The new object; or JSVAL_VOID on failure, and logs an error message
|
||||
*/
|
||||
jsval NewObjectFromConstructor(jsval ctor);
|
||||
|
||||
/**
|
||||
* Call the named property on the given object, with void return type and 0 arguments
|
||||
|
|
@ -138,6 +145,12 @@ public:
|
|||
template<typename T0, typename T1, typename T2, typename R>
|
||||
bool CallFunction(jsval val, const char* name, const T0& a0, const T1& a1, const T2& a2, R& ret);
|
||||
|
||||
/**
|
||||
* Call the named property on the given object, with return type R and 4 arguments
|
||||
*/
|
||||
template<typename T0, typename T1, typename T2, typename T3, typename R>
|
||||
bool CallFunction(jsval val, const char* name, const T0& a0, const T1& a1, const T2& a2, const T3& a3, R& ret);
|
||||
|
||||
jsval GetGlobalObject();
|
||||
|
||||
JSClass* GetGlobalClass();
|
||||
|
|
@ -155,7 +168,14 @@ public:
|
|||
* Optionally makes it {ReadOnly, DontDelete, DontEnum}.
|
||||
*/
|
||||
template<typename T>
|
||||
bool SetProperty(jsval obj, const char* name, const T& value, bool constant);
|
||||
bool SetProperty(jsval obj, const char* name, const T& value, bool constant, bool enumerate = true);
|
||||
|
||||
/**
|
||||
* Set the integer-named property on the given object.
|
||||
* Optionally makes it {ReadOnly, DontDelete, DontEnum}.
|
||||
*/
|
||||
template<typename T>
|
||||
bool SetPropertyInt(jsval obj, int name, const T& value, bool constant, bool enumerate = true);
|
||||
|
||||
template<typename T>
|
||||
bool GetProperty(jsval obj, const char* name, T& out);
|
||||
|
|
@ -166,6 +186,8 @@ public:
|
|||
|
||||
bool SetPrototype(jsval obj, jsval proto);
|
||||
|
||||
bool FreezeObject(jsval obj, bool deep);
|
||||
|
||||
bool Eval(const char* code);
|
||||
|
||||
template<typename T, typename CHAR> bool Eval(const CHAR* code, T& out);
|
||||
|
|
@ -182,6 +204,11 @@ public:
|
|||
*/
|
||||
CScriptValRooted ParseJSON(const std::string& string_utf8);
|
||||
|
||||
/**
|
||||
* Read a JSON file. Returns the undefined value on error.
|
||||
*/
|
||||
CScriptValRooted ReadJSONFile(const VfsPath& path);
|
||||
|
||||
/**
|
||||
* Stringify to a JSON string, UTF-8 encoded. Returns an empty string on error.
|
||||
*/
|
||||
|
|
@ -202,6 +229,12 @@ public:
|
|||
*/
|
||||
bool LoadScript(const std::wstring& filename, const std::wstring& code);
|
||||
|
||||
/**
|
||||
* Load and execute the given script in the global scope.
|
||||
* @return true on successful compilation and execution; false otherwise
|
||||
*/
|
||||
bool LoadGlobalScriptFile(const VfsPath& path);
|
||||
|
||||
/**
|
||||
* Construct a new value (usable in this ScriptInterface's context) by cloning
|
||||
* a value from a different context.
|
||||
|
|
@ -235,7 +268,8 @@ private:
|
|||
bool Eval_(const char* code, jsval& ret);
|
||||
bool Eval_(const wchar_t* code, jsval& ret);
|
||||
bool SetGlobal_(const char* name, jsval value, bool replace);
|
||||
bool SetProperty_(jsval obj, const char* name, jsval value, bool readonly);
|
||||
bool SetProperty_(jsval obj, const char* name, jsval value, bool readonly, bool enumerate);
|
||||
bool SetPropertyInt_(jsval obj, int name, jsval value, bool readonly, bool enumerate);
|
||||
bool GetProperty_(jsval obj, const char* name, jsval& value);
|
||||
static bool IsExceptionPending(JSContext* cx);
|
||||
static JSClass* GetClass(JSContext* cx, JSObject* obj);
|
||||
|
|
@ -344,6 +378,21 @@ bool ScriptInterface::CallFunction(jsval val, const char* name, const T0& a0, co
|
|||
return FromJSVal(GetContext(), jsRet, ret);
|
||||
}
|
||||
|
||||
template<typename T0, typename T1, typename T2, typename T3, typename R>
|
||||
bool ScriptInterface::CallFunction(jsval val, const char* name, const T0& a0, const T1& a1, const T2& a2, const T3& a3, R& ret)
|
||||
{
|
||||
jsval jsRet;
|
||||
jsval argv[4];
|
||||
argv[0] = ToJSVal(GetContext(), a0);
|
||||
argv[1] = ToJSVal(GetContext(), a1);
|
||||
argv[2] = ToJSVal(GetContext(), a2);
|
||||
argv[3] = ToJSVal(GetContext(), a3);
|
||||
bool ok = CallFunction_(val, name, 4, argv, jsRet);
|
||||
if (!ok)
|
||||
return false;
|
||||
return FromJSVal(GetContext(), jsRet, ret);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool ScriptInterface::SetGlobal(const char* name, const T& value, bool replace)
|
||||
{
|
||||
|
|
@ -351,9 +400,15 @@ bool ScriptInterface::SetGlobal(const char* name, const T& value, bool replace)
|
|||
}
|
||||
|
||||
template<typename T>
|
||||
bool ScriptInterface::SetProperty(jsval obj, const char* name, const T& value, bool readonly)
|
||||
bool ScriptInterface::SetProperty(jsval obj, const char* name, const T& value, bool readonly, bool enumerate)
|
||||
{
|
||||
return SetProperty_(obj, name, ToJSVal(GetContext(), value), readonly);
|
||||
return SetProperty_(obj, name, ToJSVal(GetContext(), value), readonly, enumerate);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool ScriptInterface::SetPropertyInt(jsval obj, int name, const T& value, bool readonly, bool enumerate)
|
||||
{
|
||||
return SetPropertyInt_(obj, name, ToJSVal(GetContext(), value), readonly, enumerate);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
|
|
|
|||
|
|
@ -32,8 +32,16 @@ public:
|
|||
CScriptVal() : m_Val(JSVAL_VOID) { }
|
||||
CScriptVal(jsval val) : m_Val(val) { }
|
||||
|
||||
/**
|
||||
* Returns the current value.
|
||||
*/
|
||||
const jsval& get() const { return m_Val; }
|
||||
|
||||
/**
|
||||
* Returns whether the value is JSVAL_VOID.
|
||||
*/
|
||||
bool undefined() const { return JSVAL_IS_VOID(m_Val); }
|
||||
|
||||
private:
|
||||
jsval m_Val;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (C) 2010 Wildfire Games.
|
||||
/* Copyright (C) 2011 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
|
|
@ -23,8 +23,9 @@
|
|||
#include "simulation2/system/ComponentManager.h"
|
||||
#include "simulation2/system/ParamNode.h"
|
||||
#include "simulation2/system/SimContext.h"
|
||||
#include "simulation2/components/ICmpTemplateManager.h"
|
||||
#include "simulation2/components/ICmpAIManager.h"
|
||||
#include "simulation2/components/ICmpCommandQueue.h"
|
||||
#include "simulation2/components/ICmpTemplateManager.h"
|
||||
|
||||
#include "lib/file/file_system_util.h"
|
||||
#include "lib/utf8.h"
|
||||
|
|
@ -55,8 +56,6 @@ public:
|
|||
RegisterFileReloadFunc(ReloadChangedFileCB, this);
|
||||
|
||||
// m_EnableOOSLog = true; // TODO: this should be a command-line flag or similar
|
||||
|
||||
// (can't call ResetState here since the scripts haven't been loaded yet)
|
||||
}
|
||||
|
||||
~CSimulation2Impl()
|
||||
|
|
@ -64,7 +63,7 @@ public:
|
|||
UnregisterFileReloadFunc(ReloadChangedFileCB, this);
|
||||
}
|
||||
|
||||
void ResetState(bool skipScriptedComponents)
|
||||
void ResetState(bool skipScriptedComponents, bool skipAI)
|
||||
{
|
||||
m_ComponentManager.ResetState();
|
||||
|
||||
|
|
@ -87,6 +86,11 @@ public:
|
|||
m_ComponentManager.AddComponent(SYSTEM_ENTITY, CID_Terrain, noParam);
|
||||
m_ComponentManager.AddComponent(SYSTEM_ENTITY, CID_WaterManager, noParam);
|
||||
|
||||
if (!skipAI)
|
||||
{
|
||||
m_ComponentManager.AddComponent(SYSTEM_ENTITY, CID_AIManager, noParam);
|
||||
}
|
||||
|
||||
// Add scripted system components:
|
||||
if (!skipScriptedComponents)
|
||||
{
|
||||
|
|
@ -96,10 +100,11 @@ public:
|
|||
LOGERROR(L"Can't find component type " L##name); \
|
||||
m_ComponentManager.AddComponent(SYSTEM_ENTITY, cid, noParam)
|
||||
|
||||
LOAD_SCRIPTED_COMPONENT("AIInterface");
|
||||
LOAD_SCRIPTED_COMPONENT("EndGameManager");
|
||||
LOAD_SCRIPTED_COMPONENT("GuiInterface");
|
||||
LOAD_SCRIPTED_COMPONENT("PlayerManager");
|
||||
LOAD_SCRIPTED_COMPONENT("Timer");
|
||||
LOAD_SCRIPTED_COMPONENT("EndGameManager");
|
||||
|
||||
#undef LOAD_SCRIPTED_COMPONENT
|
||||
}
|
||||
|
|
@ -186,6 +191,11 @@ bool CSimulation2Impl::Update(int turnLength, const std::vector<SimulationComman
|
|||
if (!cmpPathfinder.null())
|
||||
cmpPathfinder->FinishAsyncRequests();
|
||||
|
||||
// Push AI commands onto the queue before we use them
|
||||
CmpPtr<ICmpAIManager> cmpAIManager(m_SimContext, SYSTEM_ENTITY);
|
||||
if (!cmpAIManager.null())
|
||||
cmpAIManager->PushCommands();
|
||||
|
||||
CmpPtr<ICmpCommandQueue> cmpCommandQueue(m_SimContext, SYSTEM_ENTITY);
|
||||
if (!cmpCommandQueue.null())
|
||||
cmpCommandQueue->FlushTurn(commands);
|
||||
|
|
@ -223,6 +233,10 @@ bool CSimulation2Impl::Update(int turnLength, const std::vector<SimulationComman
|
|||
if (m_EnableOOSLog)
|
||||
DumpState();
|
||||
|
||||
// Start computing AI for the next turn
|
||||
if (!cmpAIManager.null())
|
||||
cmpAIManager->StartComputation();
|
||||
|
||||
++m_TurnNumber;
|
||||
|
||||
return true; // TODO: don't bother with bool return
|
||||
|
|
@ -429,9 +443,9 @@ LibError CSimulation2::ReloadChangedFile(const VfsPath& path)
|
|||
return m->ReloadChangedFile(path);
|
||||
}
|
||||
|
||||
void CSimulation2::ResetState(bool skipGui)
|
||||
void CSimulation2::ResetState(bool skipScriptedComponents, bool skipAI)
|
||||
{
|
||||
m->ResetState(skipGui);
|
||||
m->ResetState(skipScriptedComponents, skipAI);
|
||||
}
|
||||
|
||||
bool CSimulation2::ComputeStateHash(std::string& outHash)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (C) 2010 Wildfire Games.
|
||||
/* Copyright (C) 2011 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
|
|
@ -115,8 +115,11 @@ public:
|
|||
* before any methods that depend on the simulation state.
|
||||
* @param skipScriptedComponents don't load the scripted system components
|
||||
* (this is intended for use by test cases that don't mount all of VFS)
|
||||
* @param skipAI don't initialise the AI system
|
||||
* (this is intended for use by test cases that don't want all entity
|
||||
* templates loaded automatically)
|
||||
*/
|
||||
void ResetState(bool skipScriptedComponents = false);
|
||||
void ResetState(bool skipScriptedComponents = false, bool skipAI = false);
|
||||
|
||||
/**
|
||||
* Initialise a new game, based on some script data. (Called on CGame instantiation)
|
||||
|
|
|
|||
|
|
@ -57,6 +57,15 @@ COMPONENT(UnknownScript)
|
|||
|
||||
// In alphabetical order:
|
||||
|
||||
INTERFACE(AIInterface)
|
||||
COMPONENT(AIInterfaceScripted)
|
||||
|
||||
INTERFACE(AIManager)
|
||||
COMPONENT(AIManager)
|
||||
|
||||
INTERFACE(AIProxy)
|
||||
COMPONENT(AIProxyScripted)
|
||||
|
||||
INTERFACE(CommandQueue)
|
||||
COMPONENT(CommandQueue)
|
||||
|
||||
|
|
|
|||
511
source/simulation2/components/CCmpAIManager.cpp
Normal file
511
source/simulation2/components/CCmpAIManager.cpp
Normal file
|
|
@ -0,0 +1,511 @@
|
|||
/* Copyright (C) 2011 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 "simulation2/system/Component.h"
|
||||
#include "ICmpAIManager.h"
|
||||
|
||||
#include "lib/timer.h"
|
||||
#include "ps/CLogger.h"
|
||||
#include "ps/Filesystem.h"
|
||||
#include "simulation2/components/ICmpAIInterface.h"
|
||||
#include "simulation2/components/ICmpAIProxy.h"
|
||||
#include "simulation2/components/ICmpCommandQueue.h"
|
||||
#include "simulation2/components/ICmpTemplateManager.h"
|
||||
#include "simulation2/serialization/DebugSerializer.h"
|
||||
#include "simulation2/serialization/StdDeserializer.h"
|
||||
#include "simulation2/serialization/StdSerializer.h"
|
||||
#include "simulation2/serialization/SerializeTemplates.h"
|
||||
|
||||
/**
|
||||
* @file Player AI interface.
|
||||
*
|
||||
* AI is primarily scripted, and the CCmpAIManager component defined here
|
||||
* takes care of managing all the scripts.
|
||||
*
|
||||
* To avoid slow AI scripts causing jerky rendering, they are run in a background
|
||||
* thread (maintained by CAIWorker) so that it's okay if they take a whole simulation
|
||||
* turn before returning their results (though preferably they shouldn't use nearly
|
||||
* that much CPU).
|
||||
*
|
||||
* CCmpAIManager grabs the world state after each turn (making use of AIInterface.js
|
||||
* and AIProxy.js to decide what data to include) then passes it to CAIWorker.
|
||||
* The AI scripts will then run asynchronously and return a list of commands to execute.
|
||||
* Any attempts to read the command list (including indirectly via serialization)
|
||||
* will block until it's actually completed, so the rest of the engine should avoid
|
||||
* reading it for as long as possible.
|
||||
*
|
||||
* TODO: actually the thread isn't implemented yet, because performance hasn't been
|
||||
* sufficiently problematic to justify the complexity yet, but the CAIWorker interface
|
||||
* is designed to hopefully support threading when we want it.
|
||||
*/
|
||||
|
||||
class CAIWorker
|
||||
{
|
||||
private:
|
||||
struct SAIPlayer
|
||||
{
|
||||
std::wstring aiName;
|
||||
player_id_t player;
|
||||
CScriptValRooted obj;
|
||||
};
|
||||
|
||||
struct SCommands
|
||||
{
|
||||
player_id_t player;
|
||||
std::vector<CScriptValRooted> commands;
|
||||
};
|
||||
|
||||
public:
|
||||
struct SReturnedCommands
|
||||
{
|
||||
player_id_t player;
|
||||
std::vector<std::string> commands;
|
||||
};
|
||||
|
||||
CAIWorker() :
|
||||
m_ScriptInterface("Engine", "AI"),
|
||||
m_CommandsComputed(true),
|
||||
m_CurrentlyComputingPlayer(-1)
|
||||
{
|
||||
m_ScriptInterface.SetCallbackData(static_cast<void*> (this));
|
||||
|
||||
// TODO: ought to seed the RNG (in a network-synchronised way) before we use it
|
||||
m_ScriptInterface.ReplaceNondeterministicFunctions(m_RNG);
|
||||
|
||||
m_ScriptInterface.RegisterFunction<void, CScriptValRooted, CAIWorker::PostCommand>("PostCommand");
|
||||
}
|
||||
|
||||
~CAIWorker()
|
||||
{
|
||||
// Clear rooted script values before destructing the script interface
|
||||
m_EntityTemplates = CScriptValRooted();
|
||||
m_PlayerMetadata.clear();
|
||||
m_Players.clear();
|
||||
m_Commands.clear();
|
||||
}
|
||||
|
||||
bool AddPlayer(const std::wstring& aiName, player_id_t player, bool callConstructor)
|
||||
{
|
||||
std::wstring path = L"simulation/ai/" + aiName + L"/data.json";
|
||||
CScriptValRooted metadata = LoadPlayerFiles(aiName, path);
|
||||
if (metadata.uninitialised())
|
||||
{
|
||||
LOGERROR(L"Failed to create AI player: can't find %ls", path.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the constructor name from the metadata
|
||||
std::string constructor;
|
||||
if (!m_ScriptInterface.GetProperty(metadata.get(), "constructor", constructor))
|
||||
{
|
||||
LOGERROR(L"Failed to create AI player: %ls: missing 'constructor'", path.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the constructor function from the loaded scripts
|
||||
CScriptVal ctor;
|
||||
if (!m_ScriptInterface.GetProperty(m_ScriptInterface.GetGlobalObject(), constructor.c_str(), ctor)
|
||||
|| ctor.undefined())
|
||||
{
|
||||
LOGERROR(L"Failed to create AI player: %ls: can't find constructor '%hs'", path.c_str(), constructor.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
CScriptVal obj;
|
||||
|
||||
if (callConstructor)
|
||||
{
|
||||
// Set up the data to pass as the constructor argument
|
||||
CScriptVal settings;
|
||||
m_ScriptInterface.Eval(L"({})", settings);
|
||||
m_ScriptInterface.SetProperty(settings.get(), "player", player, false);
|
||||
m_ScriptInterface.SetProperty(settings.get(), "templates", m_EntityTemplates, false);
|
||||
|
||||
obj = m_ScriptInterface.CallConstructor(ctor.get(), settings.get());
|
||||
}
|
||||
else
|
||||
{
|
||||
// For deserialization, we want to create the object with the correct prototype
|
||||
// but don't want to actually run the constructor again
|
||||
obj = m_ScriptInterface.NewObjectFromConstructor(ctor.get());
|
||||
}
|
||||
|
||||
if (obj.undefined())
|
||||
{
|
||||
LOGERROR(L"Failed to create AI player: %ls: error calling constructor '%hs'", path.c_str(), constructor.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
SAIPlayer ai;
|
||||
ai.aiName = aiName;
|
||||
ai.player = player;
|
||||
ai.obj = CScriptValRooted(m_ScriptInterface.GetContext(), obj);
|
||||
m_Players.push_back(ai);
|
||||
return true;
|
||||
}
|
||||
|
||||
void StartComputation(const std::string& gameState)
|
||||
{
|
||||
debug_assert(m_CommandsComputed);
|
||||
|
||||
m_GameState = gameState;
|
||||
|
||||
m_CommandsComputed = false;
|
||||
}
|
||||
|
||||
void WaitToFinishComputation()
|
||||
{
|
||||
if (!m_CommandsComputed)
|
||||
{
|
||||
PerformComputation();
|
||||
m_CommandsComputed = true;
|
||||
}
|
||||
}
|
||||
|
||||
void GetCommands(std::vector<SReturnedCommands>& commands)
|
||||
{
|
||||
WaitToFinishComputation();
|
||||
|
||||
commands.clear();
|
||||
commands.resize(m_Commands.size());
|
||||
for (size_t i = 0; i < m_Commands.size(); ++i)
|
||||
{
|
||||
commands[i].player = m_Commands[i].player;
|
||||
commands[i].commands.resize(m_Commands[i].commands.size());
|
||||
for (size_t j = 0; j < m_Commands[i].commands.size(); ++j)
|
||||
{
|
||||
// Serialize the returned command, so that it's safe to transfer
|
||||
// across threads (in the future when we actually run AI in a
|
||||
// background thread)
|
||||
std::stringstream stream;
|
||||
CStdSerializer serializer(m_ScriptInterface, stream);
|
||||
serializer.ScriptVal("command", m_Commands[i].commands[j]);
|
||||
commands[i].commands[j] = stream.str();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LoadEntityTemplates(const std::vector<std::pair<std::string, const CParamNode*> >& templates)
|
||||
{
|
||||
m_ScriptInterface.Eval("({})", m_EntityTemplates);
|
||||
|
||||
for (size_t i = 0; i < templates.size(); ++i)
|
||||
{
|
||||
jsval val = templates[i].second->ToJSVal(m_ScriptInterface.GetContext(), false);
|
||||
m_ScriptInterface.SetProperty(m_EntityTemplates.get(), templates[i].first.c_str(), CScriptVal(val), true);
|
||||
}
|
||||
|
||||
// Since the template data is shared between AI players, freeze it
|
||||
// to stop any of them changing it and confusing the other players
|
||||
m_ScriptInterface.FreezeObject(m_EntityTemplates.get(), true);
|
||||
}
|
||||
|
||||
void Serialize(std::ostream& stream, bool isDebug)
|
||||
{
|
||||
WaitToFinishComputation();
|
||||
|
||||
if (isDebug)
|
||||
{
|
||||
CDebugSerializer serializer(m_ScriptInterface, stream);
|
||||
serializer.Indent(4);
|
||||
SerializeState(serializer);
|
||||
}
|
||||
else
|
||||
{
|
||||
CStdSerializer serializer(m_ScriptInterface, stream);
|
||||
SerializeState(serializer);
|
||||
}
|
||||
}
|
||||
|
||||
void SerializeState(ISerializer& serializer)
|
||||
{
|
||||
serializer.NumberU32_Unbounded("num ais", m_Players.size());
|
||||
|
||||
for (size_t i = 0; i < m_Players.size(); ++i)
|
||||
{
|
||||
serializer.String("name", m_Players[i].aiName, 0, 256);
|
||||
serializer.NumberI32_Unbounded("player", m_Players[i].player);
|
||||
serializer.ScriptVal("data", m_Players[i].obj);
|
||||
}
|
||||
|
||||
serializer.NumberU32_Unbounded("num ai commands", m_Commands.size());
|
||||
|
||||
for (size_t i = 0; i < m_Commands.size(); ++i)
|
||||
{
|
||||
serializer.NumberI32_Unbounded("player", m_Commands[i].player);
|
||||
SerializeVector<SerializeScriptVal>()(serializer, "commands", m_Commands[i].commands);
|
||||
}
|
||||
}
|
||||
|
||||
void Deserialize(std::istream& stream)
|
||||
{
|
||||
debug_assert(m_CommandsComputed); // deserializing while we're still actively computing would be bad
|
||||
|
||||
CStdDeserializer deserializer(m_ScriptInterface, stream);
|
||||
|
||||
m_PlayerMetadata.clear();
|
||||
m_Players.clear();
|
||||
m_Commands.clear();
|
||||
|
||||
uint32_t numAis;
|
||||
deserializer.NumberU32_Unbounded("num ais", numAis);
|
||||
|
||||
for (size_t i = 0; i < numAis; ++i)
|
||||
{
|
||||
std::wstring name;
|
||||
player_id_t player;
|
||||
deserializer.String("name", name, 0, 256);
|
||||
deserializer.NumberI32_Unbounded("player", player);
|
||||
if (!AddPlayer(name, player, false))
|
||||
throw PSERROR_Deserialize_ScriptError();
|
||||
|
||||
// Use ScriptObjectAppend so we don't lose the carefully-constructed
|
||||
// prototype/parent of this object
|
||||
deserializer.ScriptObjectAppend("data", m_Players.back().obj.getRef());
|
||||
}
|
||||
|
||||
uint32_t numCommands;
|
||||
deserializer.NumberU32_Unbounded("num ai commands", numCommands);
|
||||
|
||||
m_Commands.resize(numCommands);
|
||||
for (size_t i = 0; i < numCommands; ++i)
|
||||
{
|
||||
deserializer.NumberI32_Unbounded("player", m_Commands[i].player);
|
||||
SerializeVector<SerializeScriptVal>()(deserializer, "commands", m_Commands[i].commands);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
CScriptValRooted LoadPlayerFiles(const std::wstring& aiName, const std::wstring& path)
|
||||
{
|
||||
if (m_PlayerMetadata.find(path) == m_PlayerMetadata.end())
|
||||
{
|
||||
// Load and cache the AI player metadata
|
||||
m_PlayerMetadata[path] = m_ScriptInterface.ReadJSONFile(path);
|
||||
|
||||
// TODO: includes
|
||||
|
||||
// Load and execute *.js
|
||||
VfsPaths pathnames;
|
||||
fs_util::GetPathnames(g_VFS, L"simulation/ai/" + aiName + L"/", L"*.js", pathnames);
|
||||
for (VfsPaths::iterator it = pathnames.begin(); it != pathnames.end(); ++it)
|
||||
{
|
||||
m_ScriptInterface.LoadGlobalScriptFile(*it);
|
||||
}
|
||||
}
|
||||
|
||||
return m_PlayerMetadata[path];
|
||||
}
|
||||
|
||||
void PerformComputation()
|
||||
{
|
||||
PROFILE("AI compute");
|
||||
|
||||
m_Commands.clear();
|
||||
|
||||
// Deserialize the game state, to pass to the AI's HandleMessage
|
||||
CScriptVal state;
|
||||
|
||||
{
|
||||
// TIMER(L"deserialize AI game state");
|
||||
std::stringstream stream(m_GameState);
|
||||
CStdDeserializer deserializer(m_ScriptInterface, stream);
|
||||
deserializer.ScriptVal("state", state);
|
||||
}
|
||||
|
||||
m_ScriptInterface.FreezeObject(state.get(), true);
|
||||
|
||||
m_Commands.resize(m_Players.size());
|
||||
|
||||
for (size_t i = 0; i < m_Players.size(); ++i)
|
||||
{
|
||||
m_Commands[i].player = m_Players[i].player;
|
||||
|
||||
m_CurrentlyComputingPlayer = i;
|
||||
m_ScriptInterface.CallFunctionVoid(m_Players[i].obj.get(), "HandleMessage", state);
|
||||
// (This script will probably call PostCommand)
|
||||
}
|
||||
|
||||
m_CurrentlyComputingPlayer = -1;
|
||||
}
|
||||
|
||||
static void PostCommand(void* cbdata, CScriptValRooted cmd)
|
||||
{
|
||||
CAIWorker* self = static_cast<CAIWorker*> (cbdata);
|
||||
|
||||
debug_assert(self->m_CurrentlyComputingPlayer >= 0); // called outside of PerformComputation somehow
|
||||
|
||||
self->m_Commands[self->m_CurrentlyComputingPlayer].commands.push_back(cmd);
|
||||
}
|
||||
|
||||
ScriptInterface m_ScriptInterface;
|
||||
boost::rand48 m_RNG;
|
||||
|
||||
CScriptValRooted m_EntityTemplates;
|
||||
std::map<std::wstring, CScriptValRooted> m_PlayerMetadata;
|
||||
std::vector<SAIPlayer> m_Players;
|
||||
|
||||
std::string m_GameState;
|
||||
|
||||
bool m_CommandsComputed;
|
||||
std::vector<SCommands> m_Commands;
|
||||
int m_CurrentlyComputingPlayer; // used so PostCommand knows what player the command is for
|
||||
};
|
||||
|
||||
|
||||
|
||||
class CCmpAIManager : public ICmpAIManager
|
||||
{
|
||||
public:
|
||||
static void ClassInit(CComponentManager& UNUSED(componentManager))
|
||||
{
|
||||
}
|
||||
|
||||
DEFAULT_COMPONENT_ALLOCATOR(AIManager)
|
||||
|
||||
static std::string GetSchema()
|
||||
{
|
||||
return "<a:component type='system'/><empty/>";
|
||||
}
|
||||
|
||||
virtual void Init(const CSimContext& UNUSED(context), const CParamNode& UNUSED(paramNode))
|
||||
{
|
||||
LoadEntityTemplates();
|
||||
}
|
||||
|
||||
virtual void Deinit(const CSimContext& UNUSED(context))
|
||||
{
|
||||
}
|
||||
|
||||
virtual void Serialize(ISerializer& serialize)
|
||||
{
|
||||
// Because the AI worker uses its own ScriptInterface, we can't use the
|
||||
// ISerializer (which was initialised with the simulation ScriptInterface)
|
||||
// directly. So we'll just grab the ISerializer's stream and write to it
|
||||
// with an independent serializer.
|
||||
|
||||
m_Worker.Serialize(serialize.GetStream(), serialize.IsDebug());
|
||||
}
|
||||
|
||||
virtual void Deserialize(const CSimContext& context, const CParamNode& paramNode, IDeserializer& deserialize)
|
||||
{
|
||||
Init(context, paramNode);
|
||||
|
||||
m_Worker.Deserialize(deserialize.GetStream());
|
||||
}
|
||||
|
||||
virtual void AddPlayer(std::wstring id, player_id_t player)
|
||||
{
|
||||
m_Worker.AddPlayer(id, player, true);
|
||||
}
|
||||
|
||||
virtual void StartComputation()
|
||||
{
|
||||
PROFILE("AI setup");
|
||||
|
||||
ScriptInterface& scriptInterface = GetSimContext().GetScriptInterface();
|
||||
|
||||
CmpPtr<ICmpAIInterface> cmpAIInterface(GetSimContext(), SYSTEM_ENTITY);
|
||||
debug_assert(!cmpAIInterface.null());
|
||||
|
||||
// Get most of the game state from AIInterface
|
||||
CScriptVal state = cmpAIInterface->GetRepresentation();
|
||||
|
||||
// Get entity state from each entity:
|
||||
|
||||
CScriptVal entities;
|
||||
scriptInterface.Eval(L"({})", entities);
|
||||
|
||||
const std::map<entity_id_t, IComponent*>& ents = GetSimContext().GetComponentManager().GetEntitiesWithInterface(IID_AIProxy);
|
||||
|
||||
for (std::map<entity_id_t, IComponent*>::const_iterator it = ents.begin(); it != ents.end(); ++it)
|
||||
{
|
||||
// Skip local entities
|
||||
if (ENTITY_IS_LOCAL(it->first))
|
||||
continue;
|
||||
|
||||
CScriptVal rep = static_cast<ICmpAIProxy*>(it->second)->GetRepresentation();
|
||||
|
||||
scriptInterface.SetPropertyInt(entities.get(), it->first, rep, true);
|
||||
}
|
||||
|
||||
// Add the entities state into the object returned by AIInterface
|
||||
scriptInterface.SetProperty(state.get(), "entities", entities, true);
|
||||
|
||||
// Serialize the state representation, so that it's safe to transfer
|
||||
// across threads (in the future when we actually run AI in a
|
||||
// background thread)
|
||||
std::stringstream stream;
|
||||
CStdSerializer serializer(scriptInterface, stream);
|
||||
serializer.ScriptVal("state", state);
|
||||
|
||||
m_Worker.StartComputation(stream.str());
|
||||
}
|
||||
|
||||
virtual void PushCommands()
|
||||
{
|
||||
ScriptInterface& scriptInterface = GetSimContext().GetScriptInterface();
|
||||
|
||||
std::vector<CAIWorker::SReturnedCommands> commands;
|
||||
m_Worker.GetCommands(commands);
|
||||
|
||||
CmpPtr<ICmpCommandQueue> cmpCommandQueue(GetSimContext(), SYSTEM_ENTITY);
|
||||
if (cmpCommandQueue.null())
|
||||
return;
|
||||
|
||||
for (size_t i = 0; i < commands.size(); ++i)
|
||||
{
|
||||
for (size_t j = 0; j < commands[i].commands.size(); ++j)
|
||||
{
|
||||
std::stringstream stream(commands[i].commands[j]);
|
||||
CStdDeserializer deserializer(scriptInterface, stream);
|
||||
CScriptVal cmd;
|
||||
deserializer.ScriptVal("command", cmd);
|
||||
cmpCommandQueue->PushLocalCommand(commands[i].player, cmd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void LoadEntityTemplates()
|
||||
{
|
||||
TIMER(L"LoadEntityTemplates");
|
||||
|
||||
CmpPtr<ICmpTemplateManager> cmpTemplateManager(GetSimContext(), SYSTEM_ENTITY);
|
||||
debug_assert(!cmpTemplateManager.null());
|
||||
|
||||
std::vector<std::string> templateNames = cmpTemplateManager->FindAllTemplates(false);
|
||||
|
||||
std::vector<std::pair<std::string, const CParamNode*> > templates;
|
||||
templates.reserve(templateNames.size());
|
||||
|
||||
for (size_t i = 0; i < templateNames.size(); ++i)
|
||||
{
|
||||
const CParamNode* node = cmpTemplateManager->GetTemplate(templateNames[i]);
|
||||
if (node)
|
||||
templates.push_back(std::make_pair(templateNames[i], node));
|
||||
}
|
||||
|
||||
m_Worker.LoadEntityTemplates(templates);
|
||||
}
|
||||
|
||||
CAIWorker m_Worker;
|
||||
};
|
||||
|
||||
REGISTER_COMPONENT_TYPE(AIManager)
|
||||
|
|
@ -75,7 +75,7 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
virtual void PushLocalCommand(int player, CScriptVal cmd)
|
||||
virtual void PushLocalCommand(player_id_t player, CScriptVal cmd)
|
||||
{
|
||||
JSContext* cx = GetSimContext().GetScriptInterface().GetContext();
|
||||
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ void CCmpPathfinder::Init(const CSimContext& UNUSED(context), const CParamNode&
|
|||
// we can't use the real paramNode (it won't get handled properly when deserializing),
|
||||
// so load the data from a special XML file.
|
||||
CParamNode externalParamNode;
|
||||
CParamNode::LoadXML(externalParamNode, L"simulation/templates/special/pathfinder.xml");
|
||||
CParamNode::LoadXML(externalParamNode, L"simulation/data/pathfinder.xml");
|
||||
|
||||
|
||||
const CParamNode::ChildrenMap& passClasses = externalParamNode.GetChild("Pathfinder").GetChild("PassabilityClasses").GetChildren();
|
||||
|
|
|
|||
|
|
@ -129,7 +129,7 @@ public:
|
|||
|
||||
virtual std::string GetCurrentTemplateName(entity_id_t ent);
|
||||
|
||||
virtual std::vector<std::wstring> FindAllTemplates();
|
||||
virtual std::vector<std::string> FindAllTemplates(bool includeActors);
|
||||
|
||||
virtual std::vector<entity_id_t> GetEntitiesUsingTemplate(std::string templateName);
|
||||
|
||||
|
|
@ -360,7 +360,7 @@ void CCmpTemplateManager::ConstructTemplateActor(const std::string& actorName, C
|
|||
|
||||
static LibError AddToTemplates(const VfsPath& pathname, const FileInfo& UNUSED(fileInfo), const uintptr_t cbData)
|
||||
{
|
||||
std::vector<std::wstring>& templates = *(std::vector<std::wstring>*)cbData;
|
||||
std::vector<std::string>& templates = *(std::vector<std::string>*)cbData;
|
||||
|
||||
// Strip the .xml extension
|
||||
VfsPath pathstem = change_extension(pathname, L"");
|
||||
|
|
@ -371,28 +371,28 @@ static LibError AddToTemplates(const VfsPath& pathname, const FileInfo& UNUSED(f
|
|||
if (name.substr(0, 9) == L"template_")
|
||||
return INFO::OK;
|
||||
|
||||
templates.push_back(name);
|
||||
templates.push_back(std::string(name.begin(), name.end()));
|
||||
return INFO::OK;
|
||||
}
|
||||
|
||||
static LibError AddActorToTemplates(const VfsPath& pathname, const FileInfo& UNUSED(fileInfo), const uintptr_t cbData)
|
||||
{
|
||||
std::vector<std::wstring>& templates = *(std::vector<std::wstring>*)cbData;
|
||||
std::vector<std::string>& templates = *(std::vector<std::string>*)cbData;
|
||||
|
||||
// Strip the root from the path
|
||||
std::wstring name = pathname.string().substr(ARRAY_SIZE(ACTOR_ROOT)-1);
|
||||
|
||||
templates.push_back(L"actor|" + name);
|
||||
templates.push_back("actor|" + std::string(name.begin(), name.end()));
|
||||
return INFO::OK;
|
||||
}
|
||||
|
||||
std::vector<std::wstring> CCmpTemplateManager::FindAllTemplates()
|
||||
std::vector<std::string> CCmpTemplateManager::FindAllTemplates(bool includeActors)
|
||||
{
|
||||
// TODO: eventually this should probably read all the template files and look for flags to
|
||||
// determine which should be displayed in the editor (and in what categories etc); for now we'll
|
||||
// just return all the files
|
||||
|
||||
std::vector<std::wstring> templates;
|
||||
std::vector<std::string> templates;
|
||||
|
||||
LibError ok;
|
||||
|
||||
|
|
@ -400,9 +400,12 @@ std::vector<std::wstring> CCmpTemplateManager::FindAllTemplates()
|
|||
ok = fs_util::ForEachFile(g_VFS, TEMPLATE_ROOT, AddToTemplates, (uintptr_t)&templates, L"*.xml", fs_util::DIR_RECURSIVE);
|
||||
WARN_ERR(ok);
|
||||
|
||||
// Add all the actors too
|
||||
ok = fs_util::ForEachFile(g_VFS, ACTOR_ROOT, AddActorToTemplates, (uintptr_t)&templates, L"*.xml", fs_util::DIR_RECURSIVE);
|
||||
WARN_ERR(ok);
|
||||
if (includeActors)
|
||||
{
|
||||
// Add all the actors too
|
||||
ok = fs_util::ForEachFile(g_VFS, ACTOR_ROOT, AddActorToTemplates, (uintptr_t)&templates, L"*.xml", fs_util::DIR_RECURSIVE);
|
||||
WARN_ERR(ok);
|
||||
}
|
||||
|
||||
return templates;
|
||||
}
|
||||
|
|
|
|||
39
source/simulation2/components/ICmpAIInterface.cpp
Normal file
39
source/simulation2/components/ICmpAIInterface.cpp
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
/* Copyright (C) 2011 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 "ICmpAIInterface.h"
|
||||
|
||||
#include "simulation2/system/InterfaceScripted.h"
|
||||
#include "simulation2/scripting/ScriptComponent.h"
|
||||
|
||||
BEGIN_INTERFACE_WRAPPER(AIInterface)
|
||||
END_INTERFACE_WRAPPER(AIInterface)
|
||||
|
||||
class CCmpAIInterfaceScripted : public ICmpAIInterface
|
||||
{
|
||||
public:
|
||||
DEFAULT_SCRIPT_WRAPPER(AIInterfaceScripted)
|
||||
|
||||
virtual CScriptVal GetRepresentation()
|
||||
{
|
||||
return m_Script.Call<CScriptVal> ("GetRepresentation");
|
||||
}
|
||||
};
|
||||
|
||||
REGISTER_COMPONENT_SCRIPT_WRAPPER(AIInterfaceScripted)
|
||||
35
source/simulation2/components/ICmpAIInterface.h
Normal file
35
source/simulation2/components/ICmpAIInterface.h
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
/* Copyright (C) 2011 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_ICMPAIINTERFACE
|
||||
#define INCLUDED_ICMPAIINTERFACE
|
||||
|
||||
#include "simulation2/system/Interface.h"
|
||||
|
||||
class ICmpAIInterface : public IComponent
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Returns a script object that represents the current world state,
|
||||
* to be passed to AI scripts.
|
||||
*/
|
||||
virtual CScriptVal GetRepresentation() = 0;
|
||||
|
||||
DECLARE_INTERFACE_TYPE(AIInterface)
|
||||
};
|
||||
|
||||
#endif // INCLUDED_ICMPAIINTERFACE
|
||||
73
source/simulation2/components/ICmpAIManager.cpp
Normal file
73
source/simulation2/components/ICmpAIManager.cpp
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
/* Copyright (C) 2011 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 "ICmpAIManager.h"
|
||||
|
||||
#include "simulation2/system/InterfaceScripted.h"
|
||||
|
||||
#include "lib/file/file_system_util.h"
|
||||
#include "ps/Filesystem.h"
|
||||
|
||||
BEGIN_INTERFACE_WRAPPER(AIManager)
|
||||
DEFINE_INTERFACE_METHOD_2("AddPlayer", void, ICmpAIManager, AddPlayer, std::wstring, player_id_t)
|
||||
END_INTERFACE_WRAPPER(AIManager)
|
||||
|
||||
// Implement the static method that finds all AI scripts
|
||||
// that can be loaded via AddPlayer:
|
||||
|
||||
struct GetAIsHelper
|
||||
{
|
||||
GetAIsHelper(ScriptInterface& scriptInterface) :
|
||||
m_ScriptInterface(scriptInterface)
|
||||
{
|
||||
}
|
||||
|
||||
void Run()
|
||||
{
|
||||
fs_util::ForEachFile(g_VFS, L"simulation/ai/", Callback, (uintptr_t)this, L"*.json", fs_util::DIR_RECURSIVE);
|
||||
}
|
||||
|
||||
static LibError Callback(const VfsPath& pathname, const FileInfo& UNUSED(fileInfo), const uintptr_t cbData)
|
||||
{
|
||||
GetAIsHelper* self = (GetAIsHelper*)cbData;
|
||||
|
||||
// Extract the 3rd component of the path (i.e. the directory after simulation/ai/)
|
||||
VfsPath::iterator it = pathname.begin();
|
||||
std::advance(it, 2);
|
||||
std::wstring dirname = *it;
|
||||
|
||||
CScriptValRooted ai;
|
||||
self->m_ScriptInterface.Eval("({})", ai);
|
||||
self->m_ScriptInterface.SetProperty(ai.get(), "id", dirname, true);
|
||||
self->m_ScriptInterface.SetProperty(ai.get(), "data", self->m_ScriptInterface.ReadJSONFile(pathname), true);
|
||||
self->m_AIs.push_back(ai);
|
||||
|
||||
return INFO::OK;
|
||||
}
|
||||
|
||||
std::vector<CScriptValRooted> m_AIs;
|
||||
ScriptInterface& m_ScriptInterface;
|
||||
};
|
||||
|
||||
std::vector<CScriptValRooted> ICmpAIManager::GetAIs(ScriptInterface& scriptInterface)
|
||||
{
|
||||
GetAIsHelper helper(scriptInterface);
|
||||
helper.Run();
|
||||
return helper.m_AIs;
|
||||
}
|
||||
56
source/simulation2/components/ICmpAIManager.h
Normal file
56
source/simulation2/components/ICmpAIManager.h
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
/* Copyright (C) 2011 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_ICMPAIMANAGER
|
||||
#define INCLUDED_ICMPAIMANAGER
|
||||
|
||||
#include "simulation2/system/Interface.h"
|
||||
|
||||
#include "simulation2/helpers/Player.h"
|
||||
|
||||
class ICmpAIManager : public IComponent
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Add a new AI player into the world, based on the AI script identified
|
||||
* by @p id (corresponding to a subdirectory in simulation/ai/),
|
||||
* to control player @p player.
|
||||
*/
|
||||
virtual void AddPlayer(std::wstring id, player_id_t player) = 0;
|
||||
|
||||
/**
|
||||
* Call this at the end of a turn, to trigger AI computation which will be
|
||||
* ready for the next turn.
|
||||
*/
|
||||
virtual void StartComputation() = 0;
|
||||
|
||||
/**
|
||||
* Call this at the start of a turn, to push the computed AI commands into
|
||||
* the command queue.
|
||||
*/
|
||||
virtual void PushCommands() = 0;
|
||||
|
||||
/**
|
||||
* Returns a vector of {"id":"value-for-AddPlayer", "name":"Human readable name"}
|
||||
* objects, based on all the available AI scripts.
|
||||
*/
|
||||
static std::vector<CScriptValRooted> GetAIs(ScriptInterface& scriptInterface);
|
||||
|
||||
DECLARE_INTERFACE_TYPE(AIManager)
|
||||
};
|
||||
|
||||
#endif // INCLUDED_ICMPAIMANAGER
|
||||
39
source/simulation2/components/ICmpAIProxy.cpp
Normal file
39
source/simulation2/components/ICmpAIProxy.cpp
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
/* Copyright (C) 2011 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 "ICmpAIProxy.h"
|
||||
|
||||
#include "simulation2/system/InterfaceScripted.h"
|
||||
#include "simulation2/scripting/ScriptComponent.h"
|
||||
|
||||
BEGIN_INTERFACE_WRAPPER(AIProxy)
|
||||
END_INTERFACE_WRAPPER(AIProxy)
|
||||
|
||||
class CCmpAIProxyScripted : public ICmpAIProxy
|
||||
{
|
||||
public:
|
||||
DEFAULT_SCRIPT_WRAPPER(AIProxyScripted)
|
||||
|
||||
virtual CScriptVal GetRepresentation()
|
||||
{
|
||||
return m_Script.Call<CScriptVal> ("GetRepresentation");
|
||||
}
|
||||
};
|
||||
|
||||
REGISTER_COMPONENT_SCRIPT_WRAPPER(AIProxyScripted)
|
||||
35
source/simulation2/components/ICmpAIProxy.h
Normal file
35
source/simulation2/components/ICmpAIProxy.h
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
/* Copyright (C) 2011 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_ICMPAIPROXY
|
||||
#define INCLUDED_ICMPAIPROXY
|
||||
|
||||
#include "simulation2/system/Interface.h"
|
||||
|
||||
class ICmpAIProxy : public IComponent
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Returns a script object that represents the current entity state,
|
||||
* to be passed to AI scripts.
|
||||
*/
|
||||
virtual CScriptVal GetRepresentation() = 0;
|
||||
|
||||
DECLARE_INTERFACE_TYPE(AIProxy)
|
||||
};
|
||||
|
||||
#endif // INCLUDED_ICMPAIPROXY
|
||||
|
|
@ -22,7 +22,7 @@
|
|||
#include "simulation2/system/InterfaceScripted.h"
|
||||
|
||||
BEGIN_INTERFACE_WRAPPER(CommandQueue)
|
||||
DEFINE_INTERFACE_METHOD_2("PushLocalCommand", void, ICmpCommandQueue, PushLocalCommand, int, CScriptVal)
|
||||
DEFINE_INTERFACE_METHOD_2("PushLocalCommand", void, ICmpCommandQueue, PushLocalCommand, player_id_t, CScriptVal)
|
||||
DEFINE_INTERFACE_METHOD_1("PostNetworkCommand", void, ICmpCommandQueue, PostNetworkCommand, CScriptVal)
|
||||
// Excluded: FlushTurn (doesn't make sense for scripts to call it)
|
||||
END_INTERFACE_WRAPPER(CommandQueue)
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ public:
|
|||
/**
|
||||
* Pushes a new command onto the local queue. @p cmd does not need to be rooted.
|
||||
*/
|
||||
virtual void PushLocalCommand(int player, CScriptVal cmd) = 0;
|
||||
virtual void PushLocalCommand(player_id_t player, CScriptVal cmd) = 0;
|
||||
|
||||
/**
|
||||
* Send a command associated with the current player to the networking system.
|
||||
|
|
|
|||
|
|
@ -24,6 +24,6 @@
|
|||
BEGIN_INTERFACE_WRAPPER(TemplateManager)
|
||||
DEFINE_INTERFACE_METHOD_1("GetTemplate", const CParamNode*, ICmpTemplateManager, GetTemplate, std::string)
|
||||
DEFINE_INTERFACE_METHOD_1("GetCurrentTemplateName", std::string, ICmpTemplateManager, GetCurrentTemplateName, entity_id_t)
|
||||
DEFINE_INTERFACE_METHOD_0("FindAllTemplates", std::vector<std::wstring>, ICmpTemplateManager, FindAllTemplates)
|
||||
DEFINE_INTERFACE_METHOD_1("FindAllTemplates", std::vector<std::string>, ICmpTemplateManager, FindAllTemplates, bool)
|
||||
DEFINE_INTERFACE_METHOD_1("GetEntitiesUsingTemplate", std::vector<entity_id_t>, ICmpTemplateManager, GetEntitiesUsingTemplate, std::string)
|
||||
END_INTERFACE_WRAPPER(TemplateManager)
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ public:
|
|||
* (This includes "actor|foo" etc names).
|
||||
* Intended for use by the map editor. This is likely to be quite slow.
|
||||
*/
|
||||
virtual std::vector<std::wstring> FindAllTemplates() = 0;
|
||||
virtual std::vector<std::string> FindAllTemplates(bool includeActors) = 0;
|
||||
|
||||
/**
|
||||
* Permanently disable XML validation (intended solely for test cases).
|
||||
|
|
|
|||
|
|
@ -19,13 +19,14 @@
|
|||
#define INCLUDED_SIMULATIONCOMMAND
|
||||
|
||||
#include "scriptinterface/ScriptVal.h"
|
||||
#include "simulation2/helpers/Player.h"
|
||||
|
||||
/**
|
||||
* Simulation command, typically received over the network in multiplayer games.
|
||||
*/
|
||||
struct SimulationCommand
|
||||
{
|
||||
int player;
|
||||
player_id_t player;
|
||||
CScriptValRooted data;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -61,67 +61,15 @@ template<> jsval ScriptInterface::ToJSVal<IComponent*>(JSContext* cx, IComponent
|
|||
return OBJECT_TO_JSVAL(obj);
|
||||
}
|
||||
|
||||
static jsval ConvertCParamNode(JSContext* cx, CParamNode const& val)
|
||||
template<> jsval ScriptInterface::ToJSVal<CParamNode>(JSContext* cx, CParamNode const& val)
|
||||
{
|
||||
const std::map<std::string, CParamNode>& children = val.GetChildren();
|
||||
if (children.empty())
|
||||
{
|
||||
// Empty node - map to undefined
|
||||
if (val.ToString().empty())
|
||||
return JSVAL_VOID;
|
||||
|
||||
// Just a string
|
||||
utf16string text(val.ToString().begin(), val.ToString().end());
|
||||
JSString* str = JS_InternUCStringN(cx, reinterpret_cast<const jschar*>(text.data()), text.length());
|
||||
if (str)
|
||||
return STRING_TO_JSVAL(str);
|
||||
// TODO: report error
|
||||
return JSVAL_VOID;
|
||||
}
|
||||
|
||||
// Got child nodes - convert this node into a hash-table-style object:
|
||||
|
||||
JSObject* obj = JS_NewObject(cx, NULL, NULL, NULL);
|
||||
if (!obj)
|
||||
return JSVAL_VOID; // TODO: report error
|
||||
CScriptValRooted objRoot(cx, OBJECT_TO_JSVAL(obj));
|
||||
|
||||
for (std::map<std::string, CParamNode>::const_iterator it = children.begin(); it != children.end(); ++it)
|
||||
{
|
||||
jsval childVal = ConvertCParamNode(cx, it->second);
|
||||
if (!JS_SetProperty(cx, obj, it->first.c_str(), &childVal))
|
||||
return JSVAL_VOID; // TODO: report error
|
||||
}
|
||||
|
||||
// If the node has a string too, add that as an extra property
|
||||
if (!val.ToString().empty())
|
||||
{
|
||||
utf16string text(val.ToString().begin(), val.ToString().end());
|
||||
JSString* str = JS_InternUCStringN(cx, reinterpret_cast<const jschar*>(text.data()), text.length());
|
||||
if (!str)
|
||||
return JSVAL_VOID; // TODO: report error
|
||||
jsval childVal = STRING_TO_JSVAL(str);
|
||||
if (!JS_SetProperty(cx, obj, "_string", &childVal))
|
||||
return JSVAL_VOID; // TODO: report error
|
||||
}
|
||||
jsval rval = val.ToJSVal(cx, true);
|
||||
|
||||
// Prevent modifications to the object, so that it's safe to share between
|
||||
// components and to reconstruct on deserialization
|
||||
//JS_SealObject(cx, obj, JS_TRUE);
|
||||
// TODO: need to re-enable this when it works safely (e.g. it doesn't seal the
|
||||
// global object too (via the parent chain))
|
||||
if (JSVAL_IS_OBJECT(rval))
|
||||
JS_DeepFreezeObject(cx, JSVAL_TO_OBJECT(rval));
|
||||
|
||||
return OBJECT_TO_JSVAL(obj);
|
||||
}
|
||||
|
||||
template<> jsval ScriptInterface::ToJSVal<CParamNode>(JSContext* cx, CParamNode const& val)
|
||||
{
|
||||
// If there's a cached value, then return it
|
||||
if (!JSVAL_IS_VOID(val.GetScriptVal().get()))
|
||||
return val.GetScriptVal().get();
|
||||
|
||||
jsval rval = ConvertCParamNode(cx, val);
|
||||
val.SetScriptVal(CScriptValRooted(cx, rval));
|
||||
return rval;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,12 +27,13 @@ CComponentTypeScript::CComponentTypeScript(ScriptInterface& scriptInterface, jsv
|
|||
{
|
||||
// Cache the property detection for efficiency
|
||||
m_HasCustomSerialize = m_ScriptInterface.HasProperty(m_Instance.get(), "Serialize");
|
||||
m_HasCustomDeserialize = m_ScriptInterface.HasProperty(m_Instance.get(), "Deserialize");
|
||||
}
|
||||
|
||||
void CComponentTypeScript::Init(const CSimContext& UNUSED(context), const CParamNode& paramNode, entity_id_t ent)
|
||||
{
|
||||
m_ScriptInterface.SetProperty(m_Instance.get(), "entity", (int)ent, true);
|
||||
m_ScriptInterface.SetProperty(m_Instance.get(), "template", paramNode, true);
|
||||
m_ScriptInterface.SetProperty(m_Instance.get(), "entity", (int)ent, true, false);
|
||||
m_ScriptInterface.SetProperty(m_Instance.get(), "template", paramNode, true, false);
|
||||
m_ScriptInterface.CallFunctionVoid(m_Instance.get(), "Init");
|
||||
}
|
||||
|
||||
|
|
@ -57,7 +58,7 @@ void CComponentTypeScript::Serialize(ISerializer& serialize)
|
|||
// serialized instead of the component itself
|
||||
if (m_HasCustomSerialize)
|
||||
{
|
||||
CScriptValRooted val;
|
||||
CScriptVal val;
|
||||
if (!m_ScriptInterface.CallFunction(m_Instance.get(), "Serialize", val))
|
||||
LOGERROR(L"Script Serialize call failed");
|
||||
serialize.ScriptVal("object", val);
|
||||
|
|
@ -70,12 +71,22 @@ void CComponentTypeScript::Serialize(ISerializer& serialize)
|
|||
|
||||
void CComponentTypeScript::Deserialize(const CSimContext& UNUSED(context), const CParamNode& paramNode, IDeserializer& deserialize, entity_id_t ent)
|
||||
{
|
||||
// TODO: maybe we want to allow a script Deserialize() function, to mirror the Serialize() above
|
||||
// Support a custom "Deserialize" function, to which we pass the deserialized data
|
||||
// instead of automatically adding the deserialized properties onto the object
|
||||
if (m_HasCustomDeserialize)
|
||||
{
|
||||
CScriptVal val;
|
||||
deserialize.ScriptVal("object", val);
|
||||
if (!m_ScriptInterface.CallFunctionVoid(m_Instance.get(), "Deserialize", val))
|
||||
LOGERROR(L"Script Deserialize call failed");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use ScriptObjectAppend so we don't lose the carefully-constructed
|
||||
// prototype/parent of this object
|
||||
deserialize.ScriptObjectAppend("object", m_Instance.getRef());
|
||||
}
|
||||
|
||||
// Use ScriptObjectAppend so we don't lose the carefully-constructed
|
||||
// prototype/parent of this object
|
||||
deserialize.ScriptObjectAppend("object", m_Instance.getRef());
|
||||
|
||||
m_ScriptInterface.SetProperty(m_Instance.get(), "entity", (int)ent, true);
|
||||
m_ScriptInterface.SetProperty(m_Instance.get(), "template", paramNode, true);
|
||||
m_ScriptInterface.SetProperty(m_Instance.get(), "entity", (int)ent, true, false);
|
||||
m_ScriptInterface.SetProperty(m_Instance.get(), "template", paramNode, true, false);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,6 +76,7 @@ private:
|
|||
ScriptInterface& m_ScriptInterface;
|
||||
CScriptValRooted m_Instance;
|
||||
bool m_HasCustomSerialize;
|
||||
bool m_HasCustomDeserialize;
|
||||
|
||||
NONCOPYABLE(CComponentTypeScript);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (C) 2010 Wildfire Games.
|
||||
/* Copyright (C) 2011 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
|
|
@ -27,6 +27,26 @@
|
|||
|
||||
#include <map>
|
||||
|
||||
/**
|
||||
* Wrapper for redirecting ostream writes to CBinarySerializer's impl
|
||||
*/
|
||||
template<typename T>
|
||||
class CSerializerStreamBuf : public std::streambuf
|
||||
{
|
||||
T& m_SerializerImpl;
|
||||
public:
|
||||
CSerializerStreamBuf(T& impl) :
|
||||
m_SerializerImpl(impl)
|
||||
{
|
||||
}
|
||||
|
||||
std::streamsize xsputn(const char* s, std::streamsize n)
|
||||
{
|
||||
m_SerializerImpl.Put("stream", reinterpret_cast<const u8*> (s), n);
|
||||
return n;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* PutScriptVal implementation details.
|
||||
* (Split out from the main class because it's too big to be inlined.)
|
||||
|
|
@ -64,14 +84,18 @@ class CBinarySerializer : public ISerializer
|
|||
NONCOPYABLE(CBinarySerializer);
|
||||
public:
|
||||
CBinarySerializer(ScriptInterface& scriptInterface) :
|
||||
m_ScriptImpl(new CBinarySerializerScriptImpl(scriptInterface, *this))
|
||||
m_ScriptImpl(new CBinarySerializerScriptImpl(scriptInterface, *this)),
|
||||
m_RawStreamBuf(m_Impl),
|
||||
m_RawStream(&m_RawStreamBuf)
|
||||
{
|
||||
}
|
||||
|
||||
template <typename A>
|
||||
CBinarySerializer(ScriptInterface& scriptInterface, A& a) :
|
||||
m_ScriptImpl(new CBinarySerializerScriptImpl(scriptInterface, *this)),
|
||||
m_Impl(a)
|
||||
m_Impl(a),
|
||||
m_RawStreamBuf(m_Impl),
|
||||
m_RawStream(&m_RawStreamBuf)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -147,11 +171,19 @@ protected:
|
|||
m_Impl.Put(name, data, len);
|
||||
}
|
||||
|
||||
virtual std::ostream& GetStream()
|
||||
{
|
||||
return m_RawStream;
|
||||
}
|
||||
|
||||
protected:
|
||||
T m_Impl;
|
||||
|
||||
private:
|
||||
std::auto_ptr<CBinarySerializerScriptImpl> m_ScriptImpl;
|
||||
|
||||
CSerializerStreamBuf<T> m_RawStreamBuf;
|
||||
std::ostream m_RawStream;
|
||||
};
|
||||
|
||||
#endif // INCLUDED_BINARYSERIALIZER
|
||||
|
|
|
|||
|
|
@ -165,3 +165,8 @@ bool CDebugSerializer::IsDebug() const
|
|||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
std::ostream& CDebugSerializer::GetStream()
|
||||
{
|
||||
return m_Stream;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ public:
|
|||
void Dedent(int spaces);
|
||||
|
||||
virtual bool IsDebug() const;
|
||||
virtual std::ostream& GetStream();
|
||||
|
||||
protected:
|
||||
virtual void PutNumber(const char* name, uint8_t value);
|
||||
|
|
|
|||
|
|
@ -67,6 +67,13 @@ public:
|
|||
// Features for simulation-state serialisation:
|
||||
virtual int GetVersion() const;
|
||||
|
||||
/**
|
||||
* Returns a stream which can be used to deserialize data directly.
|
||||
* (This is particularly useful for chaining multiple deserializers
|
||||
* together.)
|
||||
*/
|
||||
virtual std::istream& GetStream() = 0;
|
||||
|
||||
protected:
|
||||
virtual void ReadString(std::string& out);
|
||||
|
||||
|
|
|
|||
|
|
@ -238,6 +238,13 @@ public:
|
|||
*/
|
||||
virtual bool IsDebug() const;
|
||||
|
||||
/**
|
||||
* Returns a stream which can be used to serialize data directly.
|
||||
* (This is particularly useful for chaining multiple serializers
|
||||
* together.)
|
||||
*/
|
||||
virtual std::ostream& GetStream() = 0;
|
||||
|
||||
protected:
|
||||
virtual void PutNumber(const char* name, uint8_t value) = 0;
|
||||
virtual void PutNumber(const char* name, int8_t value) = 0;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (C) 2010 Wildfire Games.
|
||||
/* Copyright (C) 2011 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
|
|
@ -23,6 +23,8 @@
|
|||
* Helper templates for serializing/deserializing common objects.
|
||||
*/
|
||||
|
||||
#include "simulation2/components/ICmpPathfinder.h"
|
||||
|
||||
template<typename ELEM>
|
||||
struct SerializeVector
|
||||
{
|
||||
|
|
@ -112,6 +114,19 @@ struct SerializeU32_Unbounded
|
|||
}
|
||||
};
|
||||
|
||||
struct SerializeScriptVal
|
||||
{
|
||||
void operator()(ISerializer& serialize, const char* name, CScriptValRooted value)
|
||||
{
|
||||
serialize.ScriptVal(name, value);
|
||||
}
|
||||
|
||||
void operator()(IDeserializer& deserialize, const char* name, CScriptValRooted& value)
|
||||
{
|
||||
deserialize.ScriptVal(name, value);
|
||||
}
|
||||
};
|
||||
|
||||
struct SerializeWaypoint
|
||||
{
|
||||
void operator()(ISerializer& serialize, const char* UNUSED(name), const ICmpPathfinder::Waypoint& value)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (C) 2010 Wildfire Games.
|
||||
/* Copyright (C) 2011 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
|
|
@ -45,6 +45,11 @@ void CStdDeserializer::Get(u8* data, size_t len)
|
|||
throw PSERROR_Deserialize_ReadFailed();
|
||||
}
|
||||
|
||||
std::istream& CStdDeserializer::GetStream()
|
||||
{
|
||||
return m_Stream;
|
||||
}
|
||||
|
||||
void CStdDeserializer::AddScriptBackref(JSObject* obj)
|
||||
{
|
||||
std::pair<std::map<u32, JSObject*>::iterator, bool> it = m_ScriptBackrefs.insert(std::make_pair((u32)m_ScriptBackrefs.size()+1, obj));
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (C) 2010 Wildfire Games.
|
||||
/* Copyright (C) 2011 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
|
|
@ -37,6 +37,8 @@ public:
|
|||
virtual void ScriptObjectAppend(const char* name, jsval& obj);
|
||||
virtual void ScriptString(const char* name, JSString*& out);
|
||||
|
||||
virtual std::istream& GetStream();
|
||||
|
||||
protected:
|
||||
virtual void Get(u8* data, size_t len);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (C) 2010 Wildfire Games.
|
||||
/* Copyright (C) 2011 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
|
|
@ -28,3 +28,8 @@ CStdSerializer::CStdSerializer(ScriptInterface& scriptInterface, std::ostream& s
|
|||
CBinarySerializer<CStdSerializerImpl>(scriptInterface, stream)
|
||||
{
|
||||
}
|
||||
|
||||
std::ostream& CStdSerializer::GetStream()
|
||||
{
|
||||
return m_Impl.GetStream();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (C) 2010 Wildfire Games.
|
||||
/* Copyright (C) 2011 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
|
|
@ -40,6 +40,11 @@ public:
|
|||
m_Stream.write((const char*)data, (std::streamsize)len);
|
||||
}
|
||||
|
||||
std::ostream& GetStream()
|
||||
{
|
||||
return m_Stream;
|
||||
}
|
||||
|
||||
private:
|
||||
std::ostream& m_Stream;
|
||||
};
|
||||
|
|
@ -48,6 +53,8 @@ class CStdSerializer : public CBinarySerializer<CStdSerializerImpl>
|
|||
{
|
||||
public:
|
||||
CStdSerializer(ScriptInterface& scriptInterface, std::ostream& stream);
|
||||
|
||||
virtual std::ostream& GetStream();
|
||||
};
|
||||
|
||||
#endif // INCLUDED_STDSERIALIZER
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (C) 2010 Wildfire Games.
|
||||
/* Copyright (C) 2011 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
|
|
@ -23,8 +23,8 @@
|
|||
#include "ParamNode.h"
|
||||
#include "SimContext.h"
|
||||
|
||||
#include "simulation2/components/ICmpTemplateManager.h"
|
||||
#include "simulation2/MessageTypes.h"
|
||||
#include "simulation2/components/ICmpTemplateManager.h"
|
||||
|
||||
#include "ps/CLogger.h"
|
||||
#include "ps/Filesystem.h"
|
||||
|
|
@ -582,7 +582,7 @@ IComponent* CComponentManager::ConstructComponent(entity_id_t ent, ComponentType
|
|||
jsval obj = JSVAL_NULL;
|
||||
if (ct.type == CT_Script)
|
||||
{
|
||||
obj = m_ScriptInterface.CallConstructor(ct.ctor.get());
|
||||
obj = m_ScriptInterface.CallConstructor(ct.ctor.get(), JSVAL_VOID);
|
||||
if (JSVAL_IS_VOID(obj))
|
||||
{
|
||||
LOGERROR(L"Script component constructor failed");
|
||||
|
|
@ -748,17 +748,19 @@ void CComponentManager::PostMessage(entity_id_t ent, const CMessage& msg) const
|
|||
std::vector<ComponentTypeId>::const_iterator ctit = it->second.begin();
|
||||
for (; ctit != it->second.end(); ++ctit)
|
||||
{
|
||||
// Find the component instances of this type (if any)
|
||||
std::map<ComponentTypeId, std::map<entity_id_t, IComponent*> >::const_iterator emap = m_ComponentsByTypeId.find(*ctit);
|
||||
if (emap == m_ComponentsByTypeId.end())
|
||||
continue;
|
||||
|
||||
// Send the message to all of them
|
||||
std::map<entity_id_t, IComponent*>::const_iterator eit = emap->second.find(ent);
|
||||
if (eit != emap->second.end())
|
||||
eit->second->HandleMessage(m_SimContext, msg, false);
|
||||
}
|
||||
}
|
||||
|
||||
SendGlobalMessage(msg);
|
||||
SendGlobalMessage(ent, msg);
|
||||
}
|
||||
|
||||
void CComponentManager::BroadcastMessage(const CMessage& msg) const
|
||||
|
|
@ -771,20 +773,22 @@ void CComponentManager::BroadcastMessage(const CMessage& msg) const
|
|||
std::vector<ComponentTypeId>::const_iterator ctit = it->second.begin();
|
||||
for (; ctit != it->second.end(); ++ctit)
|
||||
{
|
||||
// Find the component instances of this type (if any)
|
||||
std::map<ComponentTypeId, std::map<entity_id_t, IComponent*> >::const_iterator emap = m_ComponentsByTypeId.find(*ctit);
|
||||
if (emap == m_ComponentsByTypeId.end())
|
||||
continue;
|
||||
|
||||
// Send the message to all of them
|
||||
std::map<entity_id_t, IComponent*>::const_iterator eit = emap->second.begin();
|
||||
for (; eit != emap->second.end(); ++eit)
|
||||
eit->second->HandleMessage(m_SimContext, msg, false);
|
||||
}
|
||||
}
|
||||
|
||||
SendGlobalMessage(msg);
|
||||
SendGlobalMessage(INVALID_ENTITY, msg);
|
||||
}
|
||||
|
||||
void CComponentManager::SendGlobalMessage(const CMessage& msg) const
|
||||
void CComponentManager::SendGlobalMessage(entity_id_t ent, const CMessage& msg) const
|
||||
{
|
||||
// (Common functionality for PostMessage and BroadcastMessage)
|
||||
|
||||
|
|
@ -796,10 +800,22 @@ void CComponentManager::SendGlobalMessage(const CMessage& msg) const
|
|||
std::vector<ComponentTypeId>::const_iterator ctit = it->second.begin();
|
||||
for (; ctit != it->second.end(); ++ctit)
|
||||
{
|
||||
// Special case: Messages for non-local entities shouldn't be sent to script
|
||||
// components that subscribed globally, so that we don't have to worry about
|
||||
// them accidentally picking up non-network-synchronised data.
|
||||
if (ENTITY_IS_LOCAL(ent))
|
||||
{
|
||||
std::map<ComponentTypeId, ComponentType>::const_iterator it = m_ComponentTypesById.find(*ctit);
|
||||
if (it != m_ComponentTypesById.end() && it->second.type == CT_Script)
|
||||
continue;
|
||||
}
|
||||
|
||||
// Find the component instances of this type (if any)
|
||||
std::map<ComponentTypeId, std::map<entity_id_t, IComponent*> >::const_iterator emap = m_ComponentsByTypeId.find(*ctit);
|
||||
if (emap == m_ComponentsByTypeId.end())
|
||||
continue;
|
||||
|
||||
// Send the message to all of them
|
||||
std::map<entity_id_t, IComponent*>::const_iterator eit = emap->second.begin();
|
||||
for (; eit != emap->second.end(); ++eit)
|
||||
eit->second->HandleMessage(m_SimContext, msg, true);
|
||||
|
|
@ -875,7 +891,6 @@ std::string CComponentManager::GenerateSchema()
|
|||
|
||||
schema += "</grammar>";
|
||||
|
||||
// TODO: pretty-print
|
||||
return schema;
|
||||
}
|
||||
|
||||
|
|
@ -883,26 +898,7 @@ CScriptVal CComponentManager::Script_ReadJSONFile(void* cbdata, std::string file
|
|||
{
|
||||
CComponentManager* componentManager = static_cast<CComponentManager*> (cbdata);
|
||||
|
||||
VfsPath path = VfsPath(L"simulation/data/" + CStrW(fileName));
|
||||
|
||||
if (!FileExists(g_VFS, path))
|
||||
{
|
||||
LOGERROR(L"File 'simulation/data/%hs' does not exist", fileName.c_str());
|
||||
return CScriptVal();
|
||||
}
|
||||
|
||||
CVFSFile file;
|
||||
|
||||
PSRETURN ret = file.Load(g_VFS, path);
|
||||
|
||||
if (ret != PSRETURN_OK)
|
||||
{
|
||||
LOGERROR(L"Failed to load file '%ls': %hs", path.string().c_str(), GetErrorString(ret));
|
||||
return CScriptVal();
|
||||
}
|
||||
|
||||
std::string content(file.GetBuffer(), file.GetBuffer() + file.GetBufferSize()); // assume it's UTF-8
|
||||
|
||||
return (componentManager->m_ScriptInterface.ParseJSON(content)).get();
|
||||
std::wstring path = L"simulation/data/" + CStrW(fileName);
|
||||
|
||||
return componentManager->GetScriptInterface().ReadJSONFile(path).get();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (C) 2010 Wildfire Games.
|
||||
/* Copyright (C) 2011 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
|
|
@ -21,6 +21,7 @@
|
|||
#include "Entity.h"
|
||||
#include "Components.h"
|
||||
#include "scriptinterface/ScriptInterface.h"
|
||||
#include "simulation2/helpers/Player.h"
|
||||
|
||||
#include <boost/random/linear_congruential.hpp>
|
||||
|
||||
|
|
@ -228,7 +229,7 @@ private:
|
|||
static CScriptVal Script_ReadJSONFile(void* cbdata, std::string fileName);
|
||||
|
||||
CMessage* ConstructMessage(int mtid, CScriptVal data);
|
||||
void SendGlobalMessage(const CMessage& msg) const;
|
||||
void SendGlobalMessage(entity_id_t ent, const CMessage& msg) const;
|
||||
|
||||
ComponentTypeId GetScriptWrapper(InterfaceId iid);
|
||||
|
||||
|
|
|
|||
|
|
@ -283,15 +283,63 @@ void CParamNode::ToXML(std::wostream& strm) const
|
|||
}
|
||||
}
|
||||
|
||||
void CParamNode::SetScriptVal(CScriptValRooted val) const
|
||||
jsval CParamNode::ToJSVal(JSContext* cx, bool cacheValue) const
|
||||
{
|
||||
debug_assert(JSVAL_IS_VOID(m_ScriptVal.get()));
|
||||
m_ScriptVal = val;
|
||||
if (cacheValue && !m_ScriptVal.uninitialised())
|
||||
return m_ScriptVal.get();
|
||||
|
||||
jsval val = ConstructJSVal(cx);
|
||||
|
||||
if (cacheValue)
|
||||
m_ScriptVal = CScriptValRooted(cx, val);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
CScriptValRooted CParamNode::GetScriptVal() const
|
||||
jsval CParamNode::ConstructJSVal(JSContext* cx) const
|
||||
{
|
||||
return m_ScriptVal;
|
||||
if (m_Childs.empty())
|
||||
{
|
||||
// Empty node - map to undefined
|
||||
if (m_Value.empty())
|
||||
return JSVAL_VOID;
|
||||
|
||||
// Just a string
|
||||
utf16string text(m_Value.begin(), m_Value.end());
|
||||
JSString* str = JS_InternUCStringN(cx, reinterpret_cast<const jschar*>(text.data()), text.length());
|
||||
if (str)
|
||||
return STRING_TO_JSVAL(str);
|
||||
// TODO: report error
|
||||
return JSVAL_VOID;
|
||||
}
|
||||
|
||||
// Got child nodes - convert this node into a hash-table-style object:
|
||||
|
||||
JSObject* obj = JS_NewObject(cx, NULL, NULL, NULL);
|
||||
if (!obj)
|
||||
return JSVAL_VOID; // TODO: report error
|
||||
CScriptValRooted objRoot(cx, OBJECT_TO_JSVAL(obj));
|
||||
|
||||
for (std::map<std::string, CParamNode>::const_iterator it = m_Childs.begin(); it != m_Childs.end(); ++it)
|
||||
{
|
||||
jsval childVal = it->second.ConstructJSVal(cx);
|
||||
if (!JS_SetProperty(cx, obj, it->first.c_str(), &childVal))
|
||||
return JSVAL_VOID; // TODO: report error
|
||||
}
|
||||
|
||||
// If the node has a string too, add that as an extra property
|
||||
if (!m_Value.empty())
|
||||
{
|
||||
utf16string text(m_Value.begin(), m_Value.end());
|
||||
JSString* str = JS_InternUCStringN(cx, reinterpret_cast<const jschar*>(text.data()), text.length());
|
||||
if (!str)
|
||||
return JSVAL_VOID; // TODO: report error
|
||||
jsval childVal = STRING_TO_JSVAL(str);
|
||||
if (!JS_SetProperty(cx, obj, "_string", &childVal))
|
||||
return JSVAL_VOID; // TODO: report error
|
||||
}
|
||||
|
||||
return OBJECT_TO_JSVAL(obj);
|
||||
}
|
||||
|
||||
void CParamNode::ResetScriptVal()
|
||||
|
|
|
|||
|
|
@ -193,6 +193,17 @@ public:
|
|||
*/
|
||||
void ToXML(std::wostream& strm) const;
|
||||
|
||||
/**
|
||||
* Returns a jsval representation of this node and its children.
|
||||
* If @p cacheValue is true, then the same jsval will be returned each time
|
||||
* this is called (regardless of whether you passed the same @p cx - be careful
|
||||
* to only use the cache in one context).
|
||||
* When caching, the lifetime of @p cx must be longer than the lifetime of this node.
|
||||
* The cache will be reset if *this* node is modified (e.g. by LoadXML),
|
||||
* but *not* if any child nodes are modified (so don't do that).
|
||||
*/
|
||||
jsval ToJSVal(JSContext* cx, bool cacheValue) const;
|
||||
|
||||
/**
|
||||
* Returns the names/nodes of the children of this node, ordered by name
|
||||
*/
|
||||
|
|
@ -204,29 +215,20 @@ public:
|
|||
*/
|
||||
static std::wstring EscapeXMLString(const std::wstring& str);
|
||||
|
||||
/**
|
||||
* Stores the given script representation of this node, for use in cached conversions.
|
||||
* This must only be called once.
|
||||
* This will be reset to JSVAL_VOID if *this* node is modified (e.g. by LoadXML),
|
||||
* but *not* if any child nodes are modified (so don't do that).
|
||||
* The lifetime of the script context associated with the value must be longer
|
||||
* than the lifetime of this node.
|
||||
*/
|
||||
void SetScriptVal(CScriptValRooted val) const;
|
||||
|
||||
/**
|
||||
* Returns the value saved by SetScriptVal, or the default (JSVAL_VOID) if none was set.
|
||||
*/
|
||||
CScriptValRooted GetScriptVal() const;
|
||||
|
||||
private:
|
||||
void ApplyLayer(const XMBFile& xmb, const XMBElement& element);
|
||||
|
||||
void ResetScriptVal();
|
||||
|
||||
jsval ConstructJSVal(JSContext* cx) const;
|
||||
|
||||
std::wstring m_Value;
|
||||
ChildrenMap m_Childs;
|
||||
bool m_IsOk;
|
||||
|
||||
/**
|
||||
* Caches the ToJSVal script representation of this node.
|
||||
*/
|
||||
mutable CScriptValRooted m_ScriptVal;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (C) 2010 Wildfire Games.
|
||||
/* Copyright (C) 2011 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (C) 2010 Wildfire Games.
|
||||
/* Copyright (C) 2011 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
|
|
|
|||
|
|
@ -239,12 +239,12 @@ public:
|
|||
CmpPtr<ICmpTemplateManager> cmpTempMan(sim, SYSTEM_ENTITY);
|
||||
TS_ASSERT(!cmpTempMan.null());
|
||||
|
||||
std::vector<std::wstring> templates = cmpTempMan->FindAllTemplates();
|
||||
std::vector<std::string> templates = cmpTempMan->FindAllTemplates(true);
|
||||
for (size_t i = 0; i < templates.size(); ++i)
|
||||
{
|
||||
std::wstring name = templates[i];
|
||||
printf("# %ls\n", name.c_str());
|
||||
const CParamNode* p = cmpTempMan->GetTemplate(CStr8(name));
|
||||
std::string name = templates[i];
|
||||
printf("# %s\n", name.c_str());
|
||||
const CParamNode* p = cmpTempMan->GetTemplate(name);
|
||||
TS_ASSERT(p != NULL);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -489,7 +489,7 @@ public:
|
|||
TS_ASSERT_EQUALS(static_cast<ICmpTest1*> (man.QueryInterface(ent2, IID_Test1))->GetX(), 1+10+100+1000);
|
||||
}
|
||||
|
||||
void TODO_test_script_template_readonly()
|
||||
void test_script_template_readonly()
|
||||
{
|
||||
CSimContext context;
|
||||
CComponentManager man(context);
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ public:
|
|||
CSimulation2 sim(NULL, &m_Terrain);
|
||||
TS_ASSERT(sim.LoadScripts(L"simulation/components/addentity/"));
|
||||
|
||||
sim.ResetState(true);
|
||||
sim.ResetState(true, true);
|
||||
|
||||
entity_id_t ent1 = sim.AddEntity(L"test1");
|
||||
TS_ASSERT_EQUALS(ent1, (u32)2);
|
||||
|
|
@ -79,7 +79,7 @@ public:
|
|||
CSimulation2 sim(NULL, &m_Terrain);
|
||||
TS_ASSERT(sim.LoadScripts(L"simulation/components/addentity/"));
|
||||
|
||||
sim.ResetState(true);
|
||||
sim.ResetState(true, true);
|
||||
|
||||
entity_id_t ent1 = sim.AddEntity(L"test1");
|
||||
entity_id_t ent2 = sim.AddEntity(L"test1");
|
||||
|
|
@ -136,7 +136,7 @@ public:
|
|||
TS_ASSERT_OK(g_VFS->Invalidate(L"simulation/components/hotload/hotload.js"));
|
||||
TS_ASSERT(sim.LoadScripts(L"simulation/components/hotload/"));
|
||||
|
||||
sim.ResetState(true);
|
||||
sim.ResetState(true, true);
|
||||
|
||||
entity_id_t ent = sim.AddEntity(L"hotload");
|
||||
|
||||
|
|
|
|||
|
|
@ -80,20 +80,22 @@ QUERYHANDLER(GetObjectsList)
|
|||
CmpPtr<ICmpTemplateManager> cmp(*g_Game->GetSimulation2(), SYSTEM_ENTITY);
|
||||
if (!cmp.null())
|
||||
{
|
||||
std::vector<std::wstring> names = cmp->FindAllTemplates();
|
||||
std::vector<std::string> names = cmp->FindAllTemplates(true);
|
||||
|
||||
for (std::vector<std::wstring>::iterator it = names.begin(); it != names.end(); ++it)
|
||||
for (std::vector<std::string>::iterator it = names.begin(); it != names.end(); ++it)
|
||||
{
|
||||
std::wstring name(it->begin(), it->end());
|
||||
|
||||
sObjectsListItem e;
|
||||
e.id = *it;
|
||||
if (it->substr(0, 6) == L"actor|")
|
||||
e.id = name;
|
||||
if (name.substr(0, 6) == L"actor|")
|
||||
{
|
||||
e.name = it->substr(6);
|
||||
e.name = name.substr(6);
|
||||
e.type = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
e.name = *it;
|
||||
e.name = name;
|
||||
e.type = 0;
|
||||
}
|
||||
objects.push_back(e);
|
||||
|
|
|
|||
Loading…
Reference in a new issue