home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2007 September / maximum-cd-2007-09.iso / Assets / data / AssaultCube_v0.93.exe / source / src / savegamedemo.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2007-06-04  |  12.9 KB  |  433 lines

  1. // loading and saving of savegames & demos, dumps the spawn state of all mapents, the full state of all dynents (monsters + player)
  2.  
  3. #include "cube.h"
  4.  
  5. gzFile f = NULL;
  6. bool demorecording = false;
  7. bool demoplayback = false;
  8. bool demoloading = false;
  9. vector<playerent *> playerhistory;
  10. int democlientnum = 0;
  11.  
  12. void startdemo();
  13.  
  14. void gzcheck(int a, int b) { if(a!=b) fatal("savegame file corrupt (short)"); }
  15.  
  16. void gzput(int i) { if(gzputc(f, i)==-1) fatal("gzputc"); }
  17. void gzputi(int i) { gzcheck(gzwrite(f, &i, sizeof(int)), sizeof(int)); }
  18. void gzputv(vec &v) { gzcheck(gzwrite(f, &v, sizeof(vec)), sizeof(vec)); }
  19.  
  20. int gzget() { char c = gzgetc(f); return c; }
  21. int gzgeti() { int i; gzcheck(gzread(f, &i, sizeof(int)), sizeof(int)); return i; }
  22. void gzgetv(vec &v) { gzcheck(gzread(f, &v, sizeof(vec)), sizeof(vec)); }
  23.  
  24. void stop()
  25. {
  26.     if(f)
  27.     {
  28.         if(demorecording) gzputi(-1);
  29.         gzclose(f);
  30.     }
  31.     f = NULL;
  32.     demorecording = false;
  33.     demoplayback = false;
  34.     demoloading = false;
  35.     loopv(playerhistory) zapplayer(playerhistory[i]);
  36.     playerhistory.setsize(0);
  37.     setvar("gamespeed", 100);
  38.     extern void recomputecamera();
  39.     recomputecamera();
  40. }
  41.  
  42. void stopifrecording() { if(demorecording) stop(); }
  43.  
  44. void savestate(char *fn)
  45. {
  46.     stop();
  47.     f = gzopen(fn, "wb9");
  48.     if(!f) { conoutf("could not write %s", fn); return; }
  49.     gzwrite(f, (void *)"CUBESAVE", 8);
  50.     gzputc(f, SDL_BYTEORDER==SDL_LIL_ENDIAN ? 1 : 0);
  51.     gzputi(SAVEGAMEVERSION);
  52.     gzputi(sizeof(playerent));
  53.     gzwrite(f, getclientmap(), _MAXDEFSTR);
  54.     gzputi(gamemode);
  55.     gzputi(ents.length());
  56.     loopv(ents) gzputc(f, ents[i].spawned);
  57.     gzwrite(f, player1, sizeof(playerent));
  58.     gzputi(players.length());
  59.     loopv(players)
  60.     {
  61.         gzput(players[i]==NULL);
  62.         gzwrite(f, players[i], sizeof(playerent));
  63.     }
  64. }
  65.  
  66. void savegame(char *name)
  67. {
  68.    conoutf("can only save classic sp games"); 
  69.    return; 
  70. }
  71.  
  72. void loadstate(char *fn)
  73. {
  74.     stop();
  75.     if(multiplayer()) return;
  76.     f = gzopen(fn, "rb9");
  77.     if(!f) { conoutf("could not open %s", fn); return; }
  78.     
  79.     string buf;
  80.     gzread(f, buf, 8);
  81.     if(strncmp(buf, "CUBESAVE", 8)) goto out;
  82.     if(gzgetc(f)!=(SDL_BYTEORDER==SDL_LIL_ENDIAN ? 1 : 0)) goto out;     // not supporting save->load accross incompatible architectures simpifies things a LOT
  83.     if(gzgeti()!=SAVEGAMEVERSION || gzgeti()!=sizeof(playerent)) goto out;
  84.     string mapname;
  85.     gzread(f, mapname, _MAXDEFSTR);
  86.     nextmode = gzgeti();
  87.     changemap(mapname); // continue below once map has been loaded and client & server have updated 
  88.     return;
  89.     out:    
  90.     conoutf("aborting: savegame/demo from a different version of cube or cpu architecture");
  91.     stop();
  92. }
  93.  
  94. void loadgame(char *name)
  95. {
  96.     s_sprintfd(fn)("savegames/%s.csgz", name);
  97.     loadstate(fn);
  98. }
  99.  
  100. void loadgameout()
  101. {
  102.     stop();
  103.     conoutf("loadgame incomplete: savegame from a different version of this map");
  104. }
  105.  
  106. void loadgamerest()
  107. {
  108.     if(demoplayback || !f) return;
  109.         
  110.     if(gzgeti()!=ents.length()) return loadgameout();
  111.     loopv(ents)
  112.     {
  113.         ents[i].spawned = gzgetc(f)!=0;   
  114.     }
  115.     restoreserverstate(ents);
  116.     
  117.     gzread(f, player1, sizeof(playerent));
  118.     player1->lastaction = lastmillis;
  119.     
  120.     int nplayers = gzgeti();
  121.     loopi(nplayers) if(!gzget())
  122.     {
  123.         playerent *d = newclient(i);
  124.         ASSERT(d);
  125.         gzread(f, d, sizeof(playerent));        
  126.         d->lastaction = d->lastpain = lastmillis;
  127.     }
  128.     
  129.     conoutf("savegame restored");
  130.     if(demoloading) startdemo(); else stop();
  131. }
  132.  
  133. // demo functions
  134.  
  135. int starttime = 0;
  136. int playbacktime = 0;
  137. int ddamage, bdamage;
  138. vec dorig;
  139.  
  140. int demomillis() { return lastmillis-starttime; }
  141.  
  142. void record(char *name)
  143. {
  144.     int cn = getclientnum();
  145.     if(cn<0) return;
  146.     s_sprintfd(fn)("demos/%s.cdgz", name);
  147.     savestate(fn);
  148.     gzputi(cn);
  149.     conoutf("started recording demo to %s", fn);
  150.     demorecording = true;
  151.     starttime = lastmillis;
  152.     ddamage = bdamage = 0;
  153.     //fixme
  154.     player1->lastaction = player1->lastanimswitchtime[0] = player1->lastanimswitchtime[1] = lastmillis;
  155. }
  156.  
  157. void demodamage(int damage, vec &o) { ddamage = damage; dorig = o; }
  158. void demoblend(int damage) { bdamage = damage; }
  159.  
  160. void incomingdemodata(int chan, uchar *buf, int len, bool extras)
  161. {
  162.     if(!demorecording) return;
  163.     gzputi(lastmillis-starttime);
  164.     gzputi(len);
  165.     gzput(chan);
  166.     gzwrite(f, buf, len);
  167.     gzput(extras);
  168.     if(extras)
  169.     {
  170.         gzput(player1->gunselect);
  171.         gzput(player1->lastattackgun);
  172.         gzputi(player1->gunwait);
  173.         ASSERT(player1->lastaction == 0 || player1->lastaction-starttime >= 0);
  174.         gzputi(player1->lastaction-starttime);
  175.         gzputi(player1->lastanimswitchtime[0]-starttime);
  176.         gzputi(player1->lastanimswitchtime[1]-starttime);
  177.         loopi(NUMGUNS) { gzput(player1->ammo[i]); gzput(player1->mag[i]); }
  178.         gzput(player1->akimbo ? 1 : 0 | (player1->reloading ? 1 : 0) << 1 | (player1->weaponchanging ? 1 : 0) << 2);
  179.         switch(player1->gunselect)
  180.         {
  181.             case GUN_GRENADE:
  182.                 gzputi(player1->thrownademillis-starttime);
  183.                 break;
  184.             case GUN_PISTOL:
  185.                 if(player1->akimbo)
  186.                 {
  187.                     gzputi(player1->akimbolastaction[0]-starttime);
  188.                     gzputi(player1->akimbolastaction[1]-starttime);
  189.                 }
  190.                 break;
  191.             case GUN_SNIPER:
  192.                 gzput(scoped ? 1 : 0);
  193.                 break;
  194.         }
  195.         
  196.         gzputi(player1->health);
  197.         gzputi(player1->armour);
  198.         gzput(player1->state);
  199.         gzputi(bdamage);
  200.         bdamage = 0;
  201.         gzputi(ddamage);
  202.         if(ddamage)    { gzputv(dorig); ddamage = 0; }
  203.         // FIXME: add all other client state which is not send through the network
  204.     }
  205. }
  206.  
  207. void settingsbackup(bool save)
  208. {
  209.     static string name, team;
  210.     static int gunselect;
  211.  
  212.     if(save)
  213.     { 
  214.         s_strcpy(name, player1->name);
  215.         s_strcpy(team, player1->team);
  216.         gunselect = player1->gunselect;
  217.     }
  218.     else
  219.     {
  220.         s_strcpy(player1->name, name);
  221.         s_strcpy(player1->team, team);
  222.         player1->gunselect = gunselect;
  223.     }
  224. }
  225.  
  226. void demo(char *name)
  227. {
  228.     settingsbackup(true);
  229.     s_sprintfd(fn)("demos/%s.cdgz", name);
  230.     loadstate(fn);
  231.     demoloading = true;
  232. }
  233.  
  234. void stopreset()
  235. {
  236.     conoutf("demo stopped (%d msec elapsed)", lastmillis-starttime);
  237.     player1 = newplayerent();
  238.     disconnect(0, 0);
  239.     settingsbackup(false);
  240. }
  241.  
  242. VAR(demoplaybackspeed, 10, 100, 1000);
  243. int scaletime(int t) { return (int)(t*(100.0f/demoplaybackspeed))+starttime; }
  244.  
  245. void readdemotime()
  246. {   
  247.     if(gzeof(f) || (playbacktime = gzgeti())==-1)
  248.     {
  249.         stopreset();
  250.         return;
  251.     }
  252.     playbacktime = scaletime(playbacktime);
  253. }
  254.  
  255. bool demopaused = false;
  256. void togglepause() { demopaused = !demopaused; }
  257.  
  258. playerent *demoplayer = player1;
  259. bool firstpersondemo = true;
  260. bool localdemoplayer1st() { return demoplayback && demoplayer == player1 && firstpersondemo; }
  261.  
  262. void shiftdemoplayer(int i)
  263. {
  264.     if(!i) return;
  265.     if(demoplayer == player1) 
  266.     { 
  267.         firstpersondemo = !firstpersondemo;
  268.         if(!firstpersondemo) return;
  269.     }
  270.     vector<playerent *> plrs;
  271.     loopv(players) if(players[i] != NULL) plrs.add(players[i]);
  272.     int cur = plrs.find(demoplayer);
  273.     if(cur >= 0) demoplayer = plrs[(cur+i) % plrs.length()];
  274. }
  275.  
  276. void startdemo()
  277. {
  278.     democlientnum = gzgeti();
  279.     demoplayback = true;
  280.     starttime = lastmillis;
  281.     conoutf("now playing demo");
  282.     while(democlientnum>=players.length()) players.add(NULL);
  283.     players[democlientnum] = player1;
  284.     demoplayer = player1;
  285.     readdemotime();
  286. }
  287.  
  288. VAR(demodelaymsec, 0, 120, 500);
  289.  
  290. void catmulrom(vec &z, vec &a, vec &b, vec &c, float s, vec &dest)        // spline interpolation
  291. {
  292.     float s2 = s*s;
  293.     float s3 = s*s2;
  294.  
  295.     dest = a;
  296.     dest.mul(2*s3 - 3*s2 + 1);
  297.     dest.add(vec(b).mul(-2*s3 + 3*s2));
  298.     dest.add(vec(b).sub(z).mul(0.5f).mul(s3 - 2*s2 + s));
  299.     dest.add(vec(c).sub(a).mul(0.5f).mul(s3 - s2));
  300. }
  301.  
  302. void fixwrap(dynent *a, dynent *b)
  303. {
  304.     while(b->yaw-a->yaw>180)  a->yaw += 360;  
  305.     while(b->yaw-a->yaw<-180) a->yaw -= 360;
  306. }
  307.  
  308. void demoplaybackstep()
  309. {
  310.     while(demoplayback && lastmillis>=playbacktime)
  311.     {
  312.         int len = gzgeti();
  313.         if(len<1 || len>MAXTRANS)
  314.         {
  315.             conoutf("error: huge packet during demo play (%d)", len);
  316.             stopreset();
  317.             return;
  318.         }
  319.         int chan = gzget();
  320.         uchar buf[MAXTRANS];
  321.         gzread(f, buf, len);
  322.         localservertoclient(chan, buf, len);  // update game state
  323.         
  324.         playerent *target = (playerent *)players[democlientnum];
  325.         ASSERT(target); 
  326.         
  327.         int extras;
  328.         if((extras = gzget()))     // read additional client side state not present in normal network stream
  329.         {
  330.             target->gunselect = gzget();
  331.             target->lastattackgun = gzget();
  332.             target->gunwait = gzgeti();
  333.             target->lastaction = scaletime(gzgeti());
  334.             //ASSERT(target->lastaction >= 0);
  335.             if(target->lastaction < 0) target->lastaction = 0;
  336.             target->lastanimswitchtime[0] = scaletime(gzgeti());
  337.             target->lastanimswitchtime[1] = scaletime(gzgeti());
  338.             loopi(NUMGUNS) { target->ammo[i] = gzget(); target->mag[i] = gzget(); }
  339.             uchar flags = gzget();
  340.             target->akimbo = flags&1 ? true : false;
  341.             target->reloading = (flags>>1)&1 ? true : false;
  342.             target->weaponchanging = (flags>>2)&1 ? true : false;
  343.             switch(target->gunselect)
  344.             {
  345.                 case GUN_GRENADE:
  346.                     target->thrownademillis = scaletime(gzgeti());
  347.                     break;
  348.                 case GUN_PISTOL:
  349.                     if(player1->akimbo)
  350.                     {
  351.                         target->akimbolastaction[0] = scaletime(gzgeti());
  352.                         target->akimbolastaction[1] = scaletime(gzgeti());
  353.                     }
  354.                     break;
  355.                 case GUN_SNIPER:
  356.                     scoped = gzget() ? true : false;
  357.                     break;
  358.             }
  359.  
  360.             target->health = gzgeti();
  361.             target->armour = gzgeti();
  362.             target->state = gzget();
  363.             if((bdamage = gzgeti())) damageblend(bdamage);
  364.             if((ddamage = gzgeti())) { gzgetv(dorig); particle_splash(3, ddamage, 1000, dorig); };
  365.             // FIXME: set more client state here
  366.             target->lastmove = playbacktime;
  367.         }
  368.         
  369.         // insert latest copy of player into history
  370.         if(extras && (playerhistory.empty() || playerhistory.last()->lastupdate!=playbacktime))
  371.         {
  372.             playerent *d = newplayerent();
  373.             *d = *target;
  374.             d->lastupdate = playbacktime;
  375.             playerhistory.add(d);
  376.             if(playerhistory.length()>20)
  377.             {
  378.                 zapplayer(playerhistory[0]);
  379.                 playerhistory.remove(0);
  380.             }
  381.         }
  382.         
  383.         readdemotime();
  384.     }
  385.     
  386.     if(demoplayback)
  387.     {
  388.         if(playerhistory.length()) memcpy(player1, playerhistory.last(), sizeof(playerent));
  389.         int itime = lastmillis-demodelaymsec;
  390.         loopvrev(playerhistory) if(playerhistory[i]->lastupdate<itime)      // find 2 positions in history that surround interpolation time point
  391.         {
  392.             playerent *a = playerhistory[i];
  393.             playerent *b = a;
  394.             if(i+1<playerhistory.length()) b = playerhistory[i+1];
  395.             player1->o = b->o;
  396.             player1->yaw = b->yaw;
  397.             player1->pitch = b->pitch;
  398.             if(a!=b)                                // interpolate pos & angles
  399.             {
  400.                 dynent *c = b;
  401.                 if(i+2<playerhistory.length()) c = playerhistory[i+2];
  402.                 dynent *z = a;
  403.                 if(i-1>=0) z = playerhistory[i-1];
  404.                 //if(a==z || b==c) printf("* %d\n", lastmillis);
  405.                 float bf = (itime-a->lastupdate)/(float)(b->lastupdate-a->lastupdate);
  406.                 fixwrap(a, player1);
  407.                 fixwrap(c, player1);
  408.                 fixwrap(z, player1);
  409.                 if(z->o.dist(c->o)<16)        // if teleport or spawn, dont't interpolate
  410.                 {
  411.                     catmulrom(z->o, a->o, b->o, c->o, bf, player1->o);
  412.                     catmulrom(*(vec *)&z->yaw, *(vec *)&a->yaw, *(vec *)&b->yaw, *(vec *)&c->yaw, bf, *(vec *)&player1->yaw);
  413.                 }
  414.                 fixcamerarange(player1);
  415.             }
  416.             break;
  417.         }
  418.         //if(player1->state!=CS_DEAD) showscores(false);
  419.     }
  420. }
  421.  
  422. void stopn() { if(demoplayback) stopreset(); else stop(); conoutf("demo stopped"); }
  423.  
  424.  
  425. COMMAND(record, ARG_1STR);
  426. COMMAND(demo, ARG_1STR);
  427. COMMANDN(stop, stopn, ARG_NONE);
  428. COMMANDN(demopause, togglepause, ARG_NONE);
  429. COMMANDN(demoplayer, shiftdemoplayer, ARG_1INT);
  430.  
  431. COMMAND(savegame, ARG_1STR);
  432. COMMAND(loadgame, ARG_1STR);
  433.