Pages: 1 [2] 3 4 ... 7
  Print  
Author Topic: The server-side demos thread, rebooted!  (Read 72543 times)
GrosBedo
Member


Cakes 20
Posts: 710


« Reply #25 on: February 04, 2012, 01:21:29 pm »

Thank you Grey Matter Smiley

But lol I thought this function was used to fetch the value of a cvar. So I've got one more question: could someone tell me how to fetch the value of a cvar by name?

/EDIT: found it:

Code:
Cvar_VariableIntegerValue()

I hope this is the right way to do it!

/EDIT2: ok I changed my mind about setting sv_democlients as read-only, because then, even the engine cannot change its value, and that's bad. I think I leave it as it is for now, but in the future this cvar could even be deleted and fully managed serverside (it's already managed though).
« Last Edit: February 04, 2012, 01:49:23 pm by GrosBedo » Logged
grey matter
Member


Cakes 8
Posts: 381

>9k


« Reply #26 on: February 04, 2012, 01:57:36 pm »

Well, it depends on where you are trying to query the cvar. The engine and gamecode have slightly different cvar structures and access methods.

If you have registered (i.e. bound to a cvar_t struct) your cvar with Cvar_Get() e.g. in sv_init.c, you can then just use cvar->value, cvar->integer or cvar->string to access it directly. You should however not modify these fields directly, rather use Cvar_Set().
If you did not register the cvar, you can use Cvar_VariableIntegerValue(), Cvar_VariableString(), Cvar_VariableValue() or Cvar_VariableStringBuffer().

If you have registered (vmCvar_t) your cvar with trap_Cvar_Register() e.g. in g_main.c, you can then use cvar->value, cvar->integer or cvar->string (and trap_Cvar_Set()) as well.
If you did not register the cvar, you can use trap_Cvar_VariableIntegerValue() or trap_Cvar_VariableStringBuffer().


Regarding your edit; the engine (and gamecode using it's trap_Cvar_Set()) can set any cvars using Cvar_Set(), even those with CVAR_ROM flag. Those cvars are inteded to be changed by the game logic, but not by the user.
Logged

This space is for rent.
GrosBedo
Member


Cakes 20
Posts: 710


« Reply #27 on: February 04, 2012, 03:03:02 pm »

@grey matter: ok thank's a lot for the explanation.

Anyway about CVAR_ROM, I have to use Set_ValueLatched() (a new function that does the same as Set_Latched but with Set_Value preprocessing), and it didn't seem to work, so maybe CVAR_ROM + CVAR_LATCHED variables can't be modified even by the engine.
Logged
GrosBedo
Member


Cakes 20
Posts: 710


« Reply #28 on: February 04, 2012, 07:05:52 pm »

Takes me a little longer than expected but I'm fixing most of the bugs. I'll post the sources and patch tomorrow.

/EDIT: do someone know how to force latched values to get set?

From sv_ccmds.c:

Code:
// force latched values to get set
Cvar_Get ("g_gametype", "0", CVAR_SERVERINFO | CVAR_USERINFO | CVAR_LATCH );

//Notice that we have done a restart
sv_dorestart->integer = 0;

But it doesn't work...
Logged
GrosBedo
Member


Cakes 20
Posts: 710


« Reply #29 on: February 05, 2012, 11:59:35 am »

Files are up! You can download all the ports I've done on the first thread.

I've posted the Amanieu's port and TheDoctor's fixed patch, along with win32 binaries (I don't have a linux machine at this moment sorry, but anyway you can compile them by yourself).

Also, I've ported the Smokin' Guns patch (which is itself ported from Urban Terror) to enable /rcon tell:

Rcon tell patch for engine r28: http://zsensible.free.fr/openarena/rconpatch/rcontellpatch-engine-r28.patch
Full source with engine r28 patched: http://zsensible.free.fr/openarena/rconpatch/openarena-engine-source-0.8.x-28-rcon-tell-patch.zip
Win32 binaries: http://zsensible.free.fr/openarena/rconpatch/rcontell-engine-r28-win32binaries.zip

-----

About server-side demos, it fully work server-side, so I don't think there will be much changes. Now the problem is that demo players are NOT considered as players (this was done on purpose), but thus they are not showing on the scoreboard and so there's no way to spectate them or update the scoreboard (captures are not update even if they are announced, see the provided demo example).

I am going to be off for about 2 weeks so the project will be in standby meanwhile.

Anyway, I ask for your ideas on how to fix this scoreboard problem.

I think we have 2 ways to fix that problem:

- Patch gamecode (clientside) to correctly manage democlients
- Patch server (engine) to simulate democlients as real players

It seems TremFusion and Amanieu chose the first solution, and they patched the gamecode to correctly manage democlients. I ported the code into OAX B50, but this produce a bug (see the patch and sources in the first post). I don't know why, but I could see that even without any patch, OAX B50 prevents the player from seeing the scoreboard or join the game, so it seems OAX B50 is a bit buggy, so maybe it was not a very good idea to try to patch this version.

About the second way, I also tried but players are just empty for the engine, so I think that simulating democlients as real players would imply a huge deal of changes in the engine, because the engine want to deal with network functions with real players, where for democlients there aren't any, everything is local.

I've done as far as I could with my low knowledge of the engine, now I need some help about what is possible currently, because I don't know at all how to manage those democlients.

Anyway, please test out my serverside patch and tell me if everything works for you. Please remember that after recording serverside demos with Amanieu's code, you also need a patched binary to replay them.

Commands:
/sv_autoDemo
/demo_play
/demo_record
/demo_stop


/EDIT: I've found a bug, you must set sv_fps 25 for the demo example I posted so that it's replayed as fluently as when it was recorded (it's easily fixable by saving the sv_fps value in the demo, I'll do it later). So when replaying a demo, sv_fps must be the same as when the demo was recorded. Else, players will be a bit jerky in their movements.
« Last Edit: February 05, 2012, 03:18:02 pm by GrosBedo » Logged
GrosBedo
Member


Cakes 20
Posts: 710


« Reply #30 on: February 16, 2012, 01:59:23 pm »

I've just noticed that my patches aren't very clean, because my editor tends to automatically delete useless spaces and reformat the tabs (which is a good thing usually, but for a patch it's not).

I will post better patchs later.

/EDIT: client's gamecode patching works a bit, it's promising. See the devlog (in the 2nd post of the first page) for more details.
« Last Edit: February 16, 2012, 02:46:52 pm by GrosBedo » Logged
Neon_Knight
In the year 3000
***

Cakes 49
Posts: 3657


Trickster God.


« Reply #31 on: February 18, 2012, 06:09:34 am »

I've just remembered one thing.

If this fix would have been included in 0.8.alien it would have rendered Gig's benchmark demo as useless. :S
Logged


"Level Designers are 1 part architect, 1 part artist, 1 part game designer, and 1 part beta tester!" Cliff Bleszinski
Want to contribute? Read this.
GrosBedo
Member


Cakes 20
Posts: 710


« Reply #32 on: February 18, 2012, 06:56:12 am »

Which benchmark?
Logged
Neon_Knight
In the year 3000
***

Cakes 49
Posts: 3657


Trickster God.


« Reply #33 on: February 18, 2012, 07:31:19 am »

There's one in 0.8.alien in the demos folder inside of the pk3.
Logged


"Level Designers are 1 part architect, 1 part artist, 1 part game designer, and 1 part beta tester!" Cliff Bleszinski
Want to contribute? Read this.
GrosBedo
Member


Cakes 20
Posts: 710


« Reply #34 on: February 18, 2012, 07:35:48 am »

What's the use of this benchmark (never heard of it before)? I don't understand why my patch would be a redundancy of Gig's work, it seems goals are entirely different.

/EDIT: ah ok I think you confused the threads. I guess you're here talking about the compatfix found in this thread:
http://openarena.ws/board/index.php?topic=4443.msg43061#msg43061

Did you try it? Does it work ok for you?
Logged
Neon_Knight
In the year 3000
***

Cakes 49
Posts: 3657


Trickster God.


« Reply #35 on: February 18, 2012, 08:19:18 am »

Gig did it way before the content freeze, last year.

http://openarena.ws/board/index.php?topic=4336.0
http://openarena.ws/board/index.php?topic=1945.msg41092#msg41092
Logged


"Level Designers are 1 part architect, 1 part artist, 1 part game designer, and 1 part beta tester!" Cliff Bleszinski
Want to contribute? Read this.
GrosBedo
Member


Cakes 20
Posts: 710


« Reply #36 on: February 18, 2012, 08:29:33 am »

Thank you for the links Smiley

Please excuse me if I sound stupid, but I still don't understand the correlation with server-side demos? What must be noted is that server-side demos are managed by an entirely different code than normal demos (at least for Amanieu's code), so that server-side demos can't really be used for benchmarking (I think).
Logged
Neon_Knight
In the year 3000
***

Cakes 49
Posts: 3657


Trickster God.


« Reply #37 on: February 18, 2012, 09:00:50 am »

Oh, then I might have been confused about the 0.8.X demo compatibility. -.-
Logged


"Level Designers are 1 part architect, 1 part artist, 1 part game designer, and 1 part beta tester!" Cliff Bleszinski
Want to contribute? Read this.
GrosBedo
Member


Cakes 20
Posts: 710


« Reply #38 on: February 18, 2012, 04:59:47 pm »

Still working on the patchs, and I've got a big problem here.

The fact is, democlients get indeed put in the right team (at least they are SHOWN on the right team), but for the engine they are in fact considered as spectators! I know why they are spectators, it's in the code, all players get put to spectator at the beginning of the frame, and there's no code provided (at least not working as is) to put democlients into the right team:

The following code is an excerpt from game/g_main.c and this function is called everytime G_RunFrame is called:
Code:
void CheckDemo( void ) {
int i;

// Don't do anything if no change
if( g_demoState.integer == level.demoState ) {
return;
}
level.demoState = g_demoState.integer;

// empty teams and display a message
if( g_demoState.integer == DS_PLAYBACK ) {
trap_SendServerCommand( -1, "print \"A demo has been started on the server.\n\"" );
for( i = 0; i < level.maxclients; i++ ) {
if( level.clients[i].sess.sessionTeam != TEAM_SPECTATOR ) {
SetTeam( &g_entities[ i ], "spectator" );
}
}
}
}

As you can see, every player gets put into spectator at the beginning of the demo:
Code:
SetTeam( &g_entities[ i ], "spectator" );

But then, I don't understand why democlients (with a status of spectator) can be put in the right team (without having the status of being in the team)!

Do someone has any idea of why or how this can happen? Maybe it's in the config string (which is loaded)? But I didn't see any code that would make a player be drawn in a team without using SetTeam or .sessionTeam field... And I checked that there's no other function using SetTeam or .sessionTeam that would do this...

/EDIT: Sample of a bot config string:
Code:
n\Grism[b]\t\1\[/b]model\sarge/classic\hmodel\sarge/classic\c1\4\c2\5\hc\100\w\0\l\0\skill\ 4.00\tt\2\tl\0

So yes it seems it comes from the config string. The config string tells the engine that the player is in a team, but I expected the engine to automatically switch the player to the right team, when it doesn't. Weird.
« Last Edit: February 18, 2012, 05:28:35 pm by GrosBedo » Logged
Gig
In the year 3000
***

Cakes 48
Posts: 4237


WWW
« Reply #39 on: February 18, 2012, 06:01:41 pm »

Yes, the fix that would break compatibility with demo088-test1 (bundled with 088) is the one about 0.8.1 demo compatibility. My demo (intended to allow all players to have a common demo to use as a benchmark) was recorded before content freeze. In case a fix will disable 085 and current 088 demo compatibility, we should record a new demo to replace demo088-test1.
Logged

I never want to be aggressive, offensive or ironic with my posts. If you find something offending in my posts, read them again searching for a different mood there. If you still see something bad with them, please ask me infos. I can be wrong at times, but I never want to upset anyone.
GrosBedo
Member


Cakes 20
Posts: 710


« Reply #40 on: February 18, 2012, 06:56:10 pm »

@Gig: yes indeed, a new demo will have to be recorded with the compatibility patch. Sorry :/

@all About server-side demos, great news, IT WORKS! And the greatest news: NO DIRTY GAMECODE HACK! (ok I know gamecode hacking is better generally than engine hacking, but here I did it on purpose, because it should allow more interoperability).

Ok so now, spectating players WORKS, scoreboard WORKS, and even ping is shown (the first ping at connection, it never gets updated, but I don't think that's a big deal).

The bad news is that it still don't count the captures and don't show the chat, but replaying demos just WORKS!

I will continue to work on the code to fix the above stated issues, finish the todo list to polish the thing, and finally clean the patch from useless hacks.

@dev: I'm getting more and more the hang on the code, now I begin to understand how communication between clients and servers work, but could someone explain me a bit more about that (it will help me to enhance the patch)? I saw that configstrings commands and VM_Call are the two primary means of communication, how does that work exactly? And are there any other mean of communication?
Logged
grey matter
Member


Cakes 8
Posts: 381

>9k


« Reply #41 on: February 19, 2012, 09:09:19 am »

You're requesting an abstract picture of the whole thing while there hardly exists any documentation Smiley

Here's a very rough picture; Engine and gamecode communicate via trap or system calls. You are interested in the engine part in SV_GameSystemCalls(), which is mapped as trap_Foo() in the gamecode. The gamecode part in vmMain() is exported by QVMs and shared libs, so the engine can call the predefined functions with VM_Call()

The engine thinks in fixed time slices (controlled by sv_fps), the main logic is in Com_Frame(). SV_Frame() just calls the gamecode with GAME_RUN_FRAME.
Other gamecode callbacks are clients connecting, disconnecting, the server shutting down/restarting, client commands, client "thinks", userinfo changes etc. The gamecode calls the engine for all the complex work (file access, collision detection, etc), especially when it comes to the botlib parts.

Apart from that, the engine and gamecode don't really communicate. The engine dictates a common struct for clients, everything else is left to the gamecode (take a look at e.g. SV_LocateGameData() and all the hardcoded structs in msg.c).

Ideally, the engine should not know and care about anything the gamecode does. There are some bad examples like the map_restart code, g_needpass in infoResponse etc.
What do you mean by "configstrings commands"?
Logged

This space is for rent.
GrosBedo
Member


Cakes 20
Posts: 710


« Reply #42 on: February 19, 2012, 10:12:11 am »

Thank's a lot Grey Matter for those infos! Yes I know there's hardly any documentation (I've already read pretty much everything that exists over the web about the engine, and also read the comments in the sourcecode), that's why I asked about it here Smiley Your explanation helps me a lot!

So, if I understand correctly what you wrote, this means that communication between client and server is basically the same as a communication between engine and gamecode? That's why if I do a VM_Call, it broadcasts the message accross all clients?

About configstrings, here are a few comments I found in the sourcecode.

In bg_public.h:
Code:
// config strings are a general means of communicating variable length strings
// from the server to all connected clients.

In g_public.h:
Code:
G_SET_CONFIGSTRING, // ( int num, const char *string );
// config strings hold all the index strings, and various other information
// that is reliably communicated to all clients
// All of the current configstrings are sent to clients when
// they connect, and changes are sent to all connected clients.
// All confgstrings are cleared at each level start.
Code:
//
// functions exported by the game subsystem
//
typedef enum {
GAME_INIT, // ( int levelTime, int randomSeed, int restart );
// init and shutdown will be called every single level
// The game should call G_GET_ENTITY_TOKEN to parse through all the
// entity configuration text and spawn gentities.

GAME_SHUTDOWN, // (void);

GAME_CLIENT_CONNECT, // ( int clientNum, qboolean firstTime, qboolean isBot );
// return NULL if the client is allowed to connect, otherwise return
// a text string with the reason for denial

GAME_CLIENT_BEGIN, // ( int clientNum );

GAME_CLIENT_USERINFO_CHANGED, // ( int clientNum );

GAME_CLIENT_DISCONNECT, // ( int clientNum );

GAME_CLIENT_COMMAND, // ( int clientNum );

GAME_CLIENT_THINK, // ( int clientNum );

GAME_RUN_FRAME, // ( int levelTime );

GAME_CONSOLE_COMMAND, // ( void );
// ConsoleCommand will be called when a command has been issued
// that is not recognized as a builtin function.
// The game can issue trap_argc() / trap_argv() commands to get the command
// and parameters.  Return qfalse if the game doesn't recognize it as a command.
...
} gameExport_t;

This seems to mean that a lot of the communication between the clients and the server relies on those configstrings.

Also, as a self notes, here are some others means of communication (in g_public.h):
Code:
//
// system traps provided by the main engine
//

G_ARGC, // ( void );
// ClientCommand and ServerCommand parameter access

G_ARGV, // ( int n, char *buffer, int bufferLength );

G_SEND_CONSOLE_COMMAND, // ( const char *text );
// add commands to the console as if they were typed in
// for map changing, etc

G_SEND_SERVER_COMMAND, // ( int clientNum, const char *fmt, ... );
// reliably sends a command string to be interpreted by the given
// client.  If clientNum is -1, it will be sent to all clients

G_GET_USERINFO, // ( int num, char *buffer, int bufferSize );
// userinfo strings are maintained by the server system, so they
// are persistant across level loads, while all other game visible
// data is completely reset

Code:
//
// functions exported by the game subsystem
//

GAME_CONSOLE_COMMAND, // ( void );
// ConsoleCommand will be called when a command has been issued
// that is not recognized as a builtin function.
// The game can issue trap_argc() / trap_argv() commands to get the command
// and parameters.  Return qfalse if the game doesn't recognize it as a command.

In server.h:

Code:
//
// sv_init.c
//
void SV_SetConfigstring( int index, const char *val );
void SV_GetConfigstring( int index, char *buffer, int bufferSize );
void SV_UpdateConfigstrings( client_t *client );

void SV_SetUserinfo( int index, const char *val );
void SV_GetUserinfo( int index, char *buffer, int bufferSize );

Code:
//
// sv_snapshot.c
//
void SV_UpdateServerCommandsToClient( client_t *client, msg_t *msg );
void SV_WriteFrameToClient (client_t *client, msg_t *msg);
void SV_SendMessageToClient( msg_t *msg, client_t *client );
void SV_SendClientMessages( void );
void SV_SendClientSnapshot( client_t *client );

In sv_client.c:
Code:
SV_UpdateUserinfo_f
SV_UserinfoChanged

/EDIT: do someone know what's the use of SV_WriteFrameToClient? I could not find a concrete use of it in the code... unnecessary command?
« Last Edit: February 23, 2012, 03:06:03 pm by GrosBedo » Logged
grey matter
Member


Cakes 8
Posts: 381

>9k


« Reply #43 on: February 19, 2012, 11:05:22 am »

So, if I understand correctly what you wrote, this means that communication between client and server is basically the same as a communication between engine and gamecode? That's why if I do a VM_Call, it broadcasts the message accross all clients?
Ah, I thought you were refering to communication between engine and gamecode in one instance, e.g. a server.
Network communication works different, and to be honest I don't know much about it. There should be some documentation about it, back from the Quake 3 reverse engineering days. There are reliable commands, "normal" commands, out-of-the-box messages and so on. You should better ask this in the ioquake3 mailing list or IRC.
VM_Call() does no broadcasting, it's the generic way the engine calls functions in the gamecode (vmMain()). Do you mean something like SV_SendServerCommand(null, "foo") (a reliable command btw, also see CL_AddReliableCommand())?


This seems to mean that a lot of the communication between the clients and the server relies on those configstrings.
Configstrings are the means to communicate rather large text from server to all clients (where you don't want to use reliable commands for each client).
Engine configstrings contain information about cvars with CVAR_SYSTEMINFO and CS_SERVERINFO flag.
The gamecode maintains further configstrings for votes, items, models, sounds, players etc. The engine knowns nothing about them, it just provides the functionality to transmit them for the gamecode.

/EDIT: do someone know what's the use of SV_WriteFrameToClient? I could not find a concrete use of it in the code... unnecessary command?

SV_WriteFrameToClient() is indeed unused, only declared in headers. You can post it as a bug at ioquake3 if you wish Smiley
Logged

This space is for rent.
GrosBedo
Member


Cakes 20
Posts: 710


« Reply #44 on: February 19, 2012, 02:24:32 pm »

Thank you again Grey Matter for those infos, it enlightens me a lot.

-----

The following will contain notes about my current problem, as a note to myself in an attempt to see clearer into this, but if you are a developper and have an idea that I could try out feel free.

I have 3 current problems:
- Democlients do not get updated: if they stay in the same state as when they connected (except for frags and flagstatus that ARE updated, both on the player that holds it and on the scoreboard):
* The ping stays the same.
* If they switch team, the change is not reflected in the scoreboard, and they are not even considered to belong to the new team (an hint about this is that the team beacon do not get shown over their head - or the beacon of the other team, if they switched team during the demo recording).
* If a player or bot connects during the demo is recording, they are not shown in the scoreboard (their name is displayed in actions, you can see their player in the demo, but they can't be seen in the scoreboard - don't know if we still can spectate them?).

- Flag captures aren't displayed in the scoreboard (but they are announced both by text and by voice and sound).

- Chat messages aren't displayed (how can a player that is not updated can say anything?).

It must also be noted that if the demo started after some frags and flagcaptures were done, the frags are restored in the demo, but not the flagcaptures.

All the above leads me to think that the three problems have some correlations, and that they all points toward a scoreboard updating problem. But it seems the server do NOT really manage the scoreboard. So I'm very much puzzled. I can't even find anything about flag captures in servers files.
Logged
sago007
Posts a lot
*

Cakes 61
Posts: 1648


Open Arena Developer


WWW
« Reply #45 on: February 19, 2012, 02:43:36 pm »

Scoreboard updates and chat messages are communicated through reliable messages. Scoreboard updates are not broadcaster to all players at once but calculated for every single client. I don't know why it is calculated for every single client because it is the same message being sent to all clients. The scoreboard updates are sent in g_cmds.c in "DeathmatchScoreboardMessage".

How do you determent what reliable messages the demo watcher should receive?
Logged

There are nothing offending in my posts.
GrosBedo
Member


Cakes 20
Posts: 710


« Reply #46 on: February 20, 2012, 09:14:26 am »

The scoreboard updates are sent in g_cmds.c in "DeathmatchScoreboardMessage".

Ah ok that's what I thought (but weird that the name is "Deathmatch" when it's used for any gametype). Does it also include flagcaptures?

And do you know how I can use it in a server/ file? (eg: from sv_main.c)? I don't really know if a SV_SendServerCommand could be used to issue such a command... I'll take a look on the mechanisms anyway.

How do you determent what reliable messages the demo watcher should receive?

I don't, in fact the server-side demo records all the entities placement and actions, and also server commands and players config strings at each iteration of SV_Frame in sv_main.c. But it seems that the problem is that it doesn't record everything, so that it doesn't record when there is a ClientBegin nor a ClientUserInfoChanged, and so probably other things are not recorded.

Then, when the demo is replayed, the server tries to replay all the events that happened in the demo, by restoring their context (there's a function for that):

Code:
typedef struct cmdContext_s
{
int argc;
char *argv[ MAX_STRING_TOKENS ]; // points into cmd.tokenized
char tokenized[ BIG_INFO_STRING + MAX_STRING_TOKENS ]; // will have 0 bytes inserted
char cmd[ BIG_INFO_STRING ]; // the original command we received (no token processing)
} cmdContext_t;

static cmdContext_t cmd;
static cmdContext_t savedCmd;

/*
============
Cmd_SaveCmdContext
============
*/
void Cmd_SaveCmdContext( void )
{
Com_Memcpy( &savedCmd, &cmd, sizeof( cmdContext_t ) );
}

/*
============
Cmd_RestoreCmdContext
============
*/
void Cmd_RestoreCmdContext( void )
{
Com_Memcpy( &cmd, &savedCmd, sizeof( cmdContext_t ) );
}

I don't really understand yet how this can save the context but it seems it does the job...

After, when replaying a demo, for each demo frame, it processes all saved events:

Code:
/*
====================
SV_DemoReadFrame

Play a frame from the demo file
====================
*/
void SV_DemoReadFrame(void)
{
(...)
// Parse the message
while (1)
{
cmd = MSG_ReadByte(&msg);
switch (cmd)
{
default:
Com_Error(ERR_DROP, "SV_DemoReadFrame: Illegible demo message\n");
return;
case demo_EOF:
MSG_Clear(&msg);
goto exit_loop;
case demo_endDemo:
SV_DemoStopPlayback();
return;
case demo_endFrame:
// Overwrite anything the game may have changed
for (i = 0; i < sv.num_entities; i++)
{
if (i >= sv_democlients->integer && i < MAX_CLIENTS)
continue;
*SV_GentityNum(i) = sv.demoEntities[i];
}
for (i = 0; i < sv_democlients->integer; i++)
*SV_GameClientNum(i) = sv.demoPlayerStates[i];
// Set the server time
sv.time = MSG_ReadLong(&msg);
return;
case demo_configString:
num = MSG_ReadBits(&msg, CLIENTNUM_BITS);
tmpmsg = MSG_ReadString(&msg);
SV_SetConfigstring(CS_PLAYERS + num, tmpmsg); //, qtrue
VM_Call( gvm, GAME_CLIENT_BEGIN, num );
break;
case demo_serverCommand:
Cmd_SaveCmdContext();
tmpmsg = MSG_ReadString(&msg);
Cmd_TokenizeString(tmpmsg);
SV_SendServerCommand(NULL, "%s \"%s\"", Cmd_Argv(0), Cmd_ArgsFrom(1));
Cmd_RestoreCmdContext();
break;
case demo_gameCommand:
num = MSG_ReadByte(&msg);
Cmd_SaveCmdContext();
Cmd_TokenizeString(MSG_ReadString(&msg));
VM_Call(gvm, GAME_DEMO_COMMAND, num);
Cmd_RestoreCmdContext();
break;
case demo_playerState:
num = MSG_ReadBits(&msg, CLIENTNUM_BITS);
player = SV_GameClientNum(num);
MSG_ReadDeltaPlayerstate(&msg, &sv.demoPlayerStates[num], player);
sv.demoPlayerStates[num] = *player;
break;
case demo_entityState:
while (1)
{
num = MSG_ReadBits(&msg, GENTITYNUM_BITS);
if (num == ENTITYNUM_NONE)
break;
entity = SV_GentityNum(num);
MSG_ReadDeltaEntity(&msg, &sv.demoEntities[num].s, &entity->s, num);
sv.demoEntities[num].s = entity->s;
}
break;
case demo_entityShared:
while (1)
{
num = MSG_ReadBits(&msg, GENTITYNUM_BITS);
if (num == ENTITYNUM_NONE)
break;
entity = SV_GentityNum(num);
MSG_ReadDeltaSharedEntity(&msg, &sv.demoEntities[num].r, &entity->r, num);

// Link/unlink the entity
if (entity->r.linked && (!sv.demoEntities[num].r.linked ||
    entity->r.linkcount != sv.demoEntities[num].r.linkcount))
SV_LinkEntity(entity);
else if (!entity->r.linked && sv.demoEntities[num].r.linked)
SV_UnlinkEntity(entity);

sv.demoEntities[num].r = entity->r;
if (num > sv.num_entities)
sv.num_entities = num;
}
break;
}
}
}
}

As you can see in the case demo_configString, I simply added:

Code:
VM_Call( gvm, GAME_CLIENT_BEGIN, num );

Which is for the moment only a hack to force clients to process the democlient (and it works well), but I think the real problem is that the demo doesn't record all the events, or that it can't replay all the events.

---------------------

AH OK! I think I've got it.

In fact, the demos also relies in a later revision (the tremfusion latest revision) on a modified QVM to also save events happening in the gamecode, which are then processed when replaying the demo with a GAME_DEMO_COMMAND:

Code:
case demo_gameCommand:
num = MSG_ReadByte(&msg);
Cmd_SaveCmdContext();
Cmd_TokenizeString(MSG_ReadString(&msg));
VM_Call(gvm, GAME_DEMO_COMMAND, num);
Cmd_RestoreCmdContext();
break;

Until now, I thought that the QVM modification was only used for client-side processing of demos, but it's also used to save server-side game commands!

This is nice, but I would like to avoid hacking the gamecode as much as possible and only rely on engine, because in the end I would like this feature to be available for any mod, no matter if they load their own QVM. I'm sure there must be another way to do it.

Ok I'll try to hack further and see if this is indeed the real problem.
Logged
GrosBedo
Member


Cakes 20
Posts: 710


« Reply #47 on: February 20, 2012, 10:48:20 am »

Damn I need to add a trapped command but it keeps on crashing with the message:

Code:
********************
ERROR: Bad game system trap: 46
********************
----- Server Shutdown (Server crashed: Bad game system trap: 46) -----
==== ShutdownGame ====

I really can't understand why.

Here are the changes:
in g_main.c (this is where the trap_DemoCommand is called and is the only place, if it's commented out it does not crash anymore):
Code:
/*
============
G_DemoCommand

Store a demo command to a demo if we are recording
============
*/
void G_DemoCommand( demoCommand_t cmd, const char *string ) {
if( level.demoState == DS_RECORDING ) {
trap_DemoCommand( cmd, string );
}
}

in g_syscalls.asm:
Code:
equ trap_DemoCommand                -47

in g_syscalls.c:
Code:
void trap_DemoCommand( demoCommand_t cmd, const char *string ) {
syscall( G_DEMO_COMMAND, cmd, string );
return;
}

Of course the G_DEMO_COMMAND is also declared.

As you can see, everything is declared, and the code for this game system trap is 47, not 46! So I really don't understand why the error is not the right code number. Also, if I change the code to 49, the error is then 48! So it seems the error is always offset by -1, is this normal?
Logged
grey matter
Member


Cakes 8
Posts: 381

>9k


« Reply #48 on: February 20, 2012, 11:05:25 am »

I can't give you an explaination right now, but the offset seems to be normal behaviour Smiley
« Last Edit: February 20, 2012, 11:18:06 am by grey matter » Logged

This space is for rent.
sago007
Posts a lot
*

Cakes 61
Posts: 1648


Open Arena Developer


WWW
« Reply #49 on: February 20, 2012, 11:17:17 am »

Is the trap caught in SV_GameSystemCalls in sv_main.c?
Logged

There are nothing offending in my posts.
Pages: 1 [2] 3 4 ... 7
  Print  
 
Jump to: