1
0
forked from 0ad/0ad

# Some initial work on networking, fixing session setup, game startup, and command queueing.

- Fixed outdated / buggy networking code in the GUI scripts.
- Finished the API to CNetClient so that it's possible to start a CGame
from it.
- Some enhancements for debugging networking: Enabled updates while the
game is minimized/out-of-focus if it's in a network session. Also
reduced the turn length to something slightly more manageable but still
unplayable (1 sec versus 3 sec).
- Fixed a bug where IssueCommand used to access the order it creates
after queueing it, which is bad if the order gets deleted while being
queued (e.g. in CNetClient).

This was SVN commit r5139.
This commit is contained in:
Matei 2007-06-04 07:41:05 +00:00
parent db189468a9
commit 92578ae553
12 changed files with 100 additions and 85 deletions

View File

@ -911,20 +911,7 @@ A large landmass with rivers, forests and coastal fishing grounds.
tooltip="Click this button to start a new game with the current settings."
>Start!
<action on="Press"><![CDATA[
var losSetting = getGUIObjectByName("pgSessionSetupLosSetting").selected;
var fowEnabled = getGUIObjectByName("pgSessionSetupFoW").checked;
if(getGUIObjectByName("pgSessionSetupScreenshotMode").checked)
{
losSetting = 2;
fowEnabled = false;
}
startMap (
getCurrItemValue("pgSessionSetupMapName"),
losSetting,
fowEnabled,
getCurrItemValue("pgSessionSetupGameMode"),
getGUIObjectByName("pgSessionSetupScreenshotMode").checked,
"pgSessionSetup");
launchGame();
]]></action>
</object>

View File

@ -5,11 +5,20 @@
// ====================================================================
function startMap (mapName, losSetting, fogOfWar, gameMode, screenshotMode, openWindow)
function launchGame ()
{
// Starts the map, closing the current window.
// mapName: .pmp to load.
// openWindow: Window group (usually parent string) of control that called the function. It'll be hidden.
// Starts the map, closing the session setup window.
var mapName = getCurrItemValue("pgSessionSetupMapName");
var gameMode = getCurrItemValue("pgSessionSetupGameMode");
var screenshotMode = getGUIObjectByName("pgSessionSetupScreenshotMode").checked;
var losSetting = getGUIObjectByName("pgSessionSetupLosSetting").selected;
var fogOfWar = getGUIObjectByName("pgSessionSetupFoW").checked;
if(screenshotMode)
{
losSetting = 2;
fogOfWar = false;
}
// Check whether we have a correct file extension, to avoid crashes
var extension = mapName.substring (mapName.length, mapName.length-4);
@ -27,11 +36,13 @@ function startMap (mapName, losSetting, fogOfWar, gameMode, screenshotMode, open
g_GameAttributes.screenshotMode = screenshotMode;
// Close setup window
closeMainMenuSubWindow (openWindow);
closeMainMenuSubWindow ("pgSessionSetup");
// Display loading screen.
startLoadingScreen();
console.write( "running startGame()" );
// Begin game session.
if (! startGame())
{
@ -44,6 +55,8 @@ function startMap (mapName, losSetting, fogOfWar, gameMode, screenshotMode, open
btCode = new Array("");
messageBox(400, 200, "The game could not be started with the given parameters. You probably have entered an invalid map name.", "Error", 0, btCaptions, btCode);
}
console.write( "done running startGame()" );
// Set starting UI layout.
GUIType=rb;

View File

@ -74,26 +74,17 @@ function initMPClient (mpParentWindow, ipAddress, profileName)
// Player Name
client.playerName = profileName;
var success = client.beginConnect (ipAddress);
if (!success)
{
messageBox(400, 200, "Failed to connect to server. Please check the network connection.",
"Client Failure", 2, new Array(), new Array());
return 1;
}
else
{
console.write ("Client successfully started to connect to " + ipAddress + ".");
}
client.onClientConnect = function (event)
{
// Set player slot to new player's name.
pushItem ("pgSessionSetupP" + (event.id), event.name);
console.write("onClientConnect: name is " + event.name + ", id is " + event.id);
if( event.id != 1 )
pushItem ("pgSessionSetupP" + (event.id), event.name);
}
client.onConnectComplete = function (event)
{
console.write("onConnectComplete: " + event.message);
if (event.success)
{
// Switch to Session Setup screen.
@ -118,29 +109,34 @@ function initMPClient (mpParentWindow, ipAddress, profileName)
client.onDisconnect = function (event)
{
console.write("onDisconnect: " + event.message);
messageBox (400, 200, event.message,
"Host Disconnected", 2, new Array(), new Array());
}
client.onClientDisconnect = function (event)
{
console.write("onClientDisconnect: " + event.message);
messageBox (400, 200, event.session,
"Client " + event.name + "(" + event.id + ") Disconnected", 2, new Array(), new Array());
}
client.onConnect = function (event)
{
messageBox (400, 200, event.session,
"Client " + event.name + "(" + event.id + ") Connected", 2, new Array(), new Array());
}
client.onStartGame = function (event)
{
// The server has called Start Game!, so we better switch to the session GUI and stuff like that.
startMap (
getCurrItemValue ("pgSessionSetupMapName"),
getGUIObjectByName("pgSessionSetupLosSetting").selected,
"pgSessionSetup");
launchGame();
}
var success = client.beginConnect (ipAddress);
if (!success)
{
messageBox(400, 200, "Failed to connect to server. Please check the network connection.",
"Client Failure", 2, new Array(), new Array());
return 1;
}
else
{
console.write ("Client successfully started to connect to " + ipAddress + ".");
}
}

View File

@ -42,7 +42,7 @@ function openSessionSetup (sessionReturnWindow)
// Open the slot.
g_GameAttributes.slots[slotNumber-1].assignOpen();
console.write ("Opened slot " + this.name);
//console.write ("Opened slot " + this.name);
break;
case "Closed":
// If "Closed" selected,
@ -61,7 +61,7 @@ function openSessionSetup (sessionReturnWindow)
// Close the slot.
g_GameAttributes.slots[slotNumber-1].assignClosed();
console.write ("Closed slot " + this.name);
//console.write ("Closed slot " + this.name);
break;
default:
break;

View File

@ -32,6 +32,8 @@ that of Atlas depending on commandline parameters.
#include "ps/Hotkey.h"
#include "ps/Globals.h"
#include "ps/Interact.h"
#include "network/Client.h"
#include "network/Server.h"
#include "network/SessionManager.h"
#include "graphics/Camera.h"
#include "graphics/GameView.h"
@ -175,31 +177,19 @@ static void Frame()
debug_assert(TimeSinceLastFrame >= 0.0f);
// decide if update/render is necessary
bool need_render, need_update;
if( g_app_minimized )
{
// TODO: eventually update ought to be re-enabled so the server host
// can Alt+Tab out without the match hanging. however, game updates
// are currently really slow and disabling them makes debugging nicer.
need_update = false;
need_render = false;
bool need_render = !g_app_minimized;
bool need_update = true;
// inactive; relinquish CPU for a little while
// If we are not running a multiplayer game, disable updates when the game is
// minimized or out of focus and relinquish the CPU a bit, in order to make
// debugging easier.
if( !g_NetClient && !g_NetServer && !g_app_has_focus )
{
need_update = false;
// don't use SDL_WaitEvent: don't want the main loop to freeze until app focus is restored
SDL_Delay(10);
}
else if( !g_app_has_focus )
{
need_update = false; // see above
need_render = true;
SDL_Delay(5); // see above
}
else // active
{
need_update = true;
need_render = true;
}
// TODO: throttling: limit update and render frequency to the minimum.
// this is mostly relevant for "inactive" state, so that other windows
// get enough CPU time, but it's always nice for power+thermal management.

View File

@ -252,7 +252,7 @@ bool CNetClient::PreGameHandler(CNetMessage *pMsg, CNetSession *pSession)
{
case NMT_StartGame:
{
pClient->StartGame();
pClient->OnStartGameMessage();
HANDLED(pMsg);
}
case NMT_ClientConnect:
@ -425,16 +425,29 @@ void CNetClient::OnClientDisconnect(int sessionID)
m_ServerSessions.erase(it);
}
void CNetClient::StartGame()
void CNetClient::OnStartGameMessage()
{
m_pMessageHandler=InGameHandler;
m_pGame->StartGame(m_pGameAttributes);
if (m_OnStartGame.Defined())
debug_assert( m_OnStartGame.Defined() );
CStartGameEvent evt;
m_OnStartGame.DispatchEvent(GetScript(), &evt);
}
int CNetClient::StartGame()
{
if (m_pGame->StartGame(m_pGameAttributes) != PSRETURN_OK)
{
CStartGameEvent evt;
m_OnStartGame.DispatchEvent(GetScript(), &evt);
return -1;
}
else
{
return 0;
}
}
CPlayer* CNetClient::GetLocalPlayer()
{
return m_pLocalPlayerSlot->GetPlayer();
}
void CNetClient::NewTurn()

View File

@ -50,6 +50,7 @@ class CNetClient: public CNetSession, protected CTurnManager, public CJSObject<C
void OnClientConnect(int sessionID, const CStrW& name);
void OnClientDisconnect(int sessionID);
void OnStartGameMessage();
// JS Interface Functions
bool JSI_BeginConnect(JSContext *cx, uintN argc, jsval *argv);
@ -59,12 +60,16 @@ protected:
virtual void NewTurn();
virtual void QueueLocalCommand(CNetMessage *pMsg);
void StartGame();
public:
CNetClient(CGame *pGame, CGameAttributes *pGameAttribs);
virtual ~CNetClient();
// Launch a game through this client
int StartGame();
// Get a pointer to our player
CPlayer* GetLocalPlayer();
static MessageHandler ConnectHandler;
static MessageHandler BaseHandler; // Common to all connected states

View File

@ -234,7 +234,7 @@ void CMessageSocket::StartReadHeader()
m_pRdBuffer=(u8 *)malloc(m_RdBufferSize);
}
m_ReadingData=false;
//printf("CMessageSocket::StartReadHeader(): Trying to read %u\n", HEADER_LENGTH);
printf("CMessageSocket::StartReadHeader(): Trying to read %u\n", HEADER_LENGTH);
PS_RESULT res=Read(m_pRdBuffer, HEADER_LENGTH);
if (res != PS_OK)
{
@ -260,7 +260,7 @@ void CMessageSocket::StartReadMessage()
m_pRdBuffer=(u8 *)malloc(m_RdBufferSize);
}
m_ReadingData=true;
//printf("CMessageSocket::StartReadMessage(): Got type %d, trying to read %u\n", hdr.m_MsgType, hdr.m_MsgLength);
printf("CMessageSocket::StartReadMessage(): Got type %d, trying to read %u\n", hdr.m_MsgType, hdr.m_MsgLength);
if (hdr.m_MsgLength == 0)
{

View File

@ -65,9 +65,10 @@ CNetServer::CNetServer(CGame *pGame, CGameAttributes *pGameAttribs):
m_pGameAttributes->SetPlayerSlotAssignmentCallback(PlayerSlotAssignmentCallback, this);
m_pGame->GetSimulation()->SetTurnManager(this);
// Set an incredibly long turn length - less command batch spam that way
// Set an incredibly long turn length for debugging - less command batch spam that way
for (int i=0;i<3;i++)
CTurnManager::SetTurnLength(i, 3000);
CTurnManager::SetTurnLength(i, 1000);
g_ScriptingHost.SetGlobal("g_NetServer", OBJECT_TO_JSVAL(GetScript()));
}
@ -348,7 +349,9 @@ int CNetServer::StartGame()
Broadcast(new CStartGame());
if (m_pGame->StartGame(m_pGameAttributes) != PSRETURN_OK)
{
return -1;
}
else
{
CTurnManager::Initialize(m_pGameAttributes->GetSlotCount());

View File

@ -277,7 +277,7 @@ PS_RESULT CSocketBase::Initialize(ESocketProtocol proto)
g_SocketSetInternal.m_NumSockets++;
GLOBAL_UNLOCK();
SetNonBlocking(true);
SetNonBlocking(m_NonBlocking);
return PS_OK;
}

View File

@ -180,7 +180,7 @@ PSRETURN CGame::StartGame(CGameAttributes *pAttribs)
if (g_NetClient)
{
// TODO
//m_pLocalPlayer=g_NetClient->GetLocalPlayer();
m_pLocalPlayer = g_NetClient->GetLocalPlayer();
debug_assert(m_pLocalPlayer && "Darn it! We weren't assigned to a slot!");
}
else

View File

@ -237,17 +237,18 @@ JSBool IssueCommand( JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* rv
entities.push_back( (ToNative<CEntity>(argv[0])) ->me);
else
entities = *EntityCollection::RetrieveSet(cx, JSVAL_TO_OBJECT(argv[0]));
std::map<int, CEntityList> entityStore;
bool isQueued = ToPrimitive<bool>(argv[1]);
//Destroy old notifiers if we're explicitly being reassigned
for ( size_t i=0; i < entities.size(); i++)
{
if ( entities[i]->entf_get(ENTF_DESTROY_NOTIFIERS))
entities[i]->DestroyAllNotifiers();
}
std::vector<CNetMessage*> messages;
//Generate messages for formations
@ -281,9 +282,9 @@ JSBool IssueCommand( JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* rv
for ( std::vector<CNetMessage*>::iterator it=messages.begin(); it != messages.end(); it++ )
{
//g_Console->InsertMessage(L"IssueCommand: %hs", (*it)->GetString().c_str());
g_Game->GetSimulation()->QueueLocalCommand(*it);
g_Console->InsertMessage(L"IssueCommand: %hs", (*it)->GetString().c_str());
*rval = g_ScriptingHost.UCStringToValue((*it)->GetString());
g_Game->GetSimulation()->QueueLocalCommand(*it);
}
return JS_TRUE;
@ -728,11 +729,14 @@ JSBool StartGame(JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* rval)
*rval = BOOLEAN_TO_JSVAL(JS_TRUE);
// Hosted MP Game
if (g_NetServer)
if (g_NetServer)
{
*rval = BOOLEAN_TO_JSVAL(g_NetServer->StartGame() == 0);
// Joined MP Game: StartGame is invalid - do nothing
}
// Joined MP Game
else if (g_NetClient)
{
*rval = BOOLEAN_TO_JSVAL(g_NetClient->StartGame() == 0);
}
// Start an SP Game Session
else if (!g_Game)
@ -750,6 +754,10 @@ JSBool StartGame(JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* rval)
return( JS_TRUE );
}
}
else
{
*rval = BOOLEAN_TO_JSVAL(JS_FALSE);
}
return( JS_TRUE );
}