PageRenderTime 54ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 0ms

/OpenLieroX/src/game/GameState.cpp

#
C++ | 400 lines | 325 code | 44 blank | 31 comment | 80 complexity | 7da42e10159964bafdddddb2b52cfa47 MD5 | raw file
Possible License(s): LGPL-2.0, LGPL-2.1, GPL-2.0
  1. /*
  2. * GameState.cpp
  3. * OpenLieroX
  4. *
  5. * Created by Albert Zeyer on 15.02.12.
  6. * code under LGPL
  7. *
  8. */
  9. #include "GameState.h"
  10. #include "Game.h"
  11. #include "Settings.h"
  12. #include "CWorm.h"
  13. #include "CBytestream.h"
  14. #include "ClassInfo.h"
  15. #include "ProfileSystem.h"
  16. #include "CServerConnection.h"
  17. #include "game/Attr.h"
  18. ScriptVar_t ObjectState::getValue(AttribRef a) const {
  19. Attribs::const_iterator it = attribs.find(a);
  20. if(it == attribs.end())
  21. return a.getAttrDesc()->defaultValue;
  22. return it->second.value;
  23. }
  24. GameStateUpdates::operator bool() const {
  25. if(!objs.empty()) return true;
  26. if(!objCreations.empty()) return true;
  27. if(!objDeletions.empty()) return true;
  28. return false;
  29. }
  30. void GameStateUpdates::writeToBs(CBytestream* bs, const GameState& oldState) const {
  31. bs->writeInt16((uint16_t)objCreations.size());
  32. const_foreach(o, objCreations) {
  33. o->writeToBs(bs);
  34. }
  35. bs->writeInt16((uint16_t)objDeletions.size());
  36. const_foreach(o, objDeletions) {
  37. o->writeToBs(bs);
  38. }
  39. bs->writeInt((uint32_t)objs.size(), 4);
  40. const_foreach(a, objs) {
  41. const ObjAttrRef& attr = *a;
  42. ScriptVar_t curValue = a->get();
  43. attr.writeToBs(bs);
  44. if((attr.attr.getAttrDesc()->attrType == SVT_CustomWeakRefToStatic || attr.attr.getAttrDesc()->attrType == SVT_CUSTOM) && oldState.haveObject(attr.obj)) {
  45. ScriptVar_t oldValue = oldState.getValue(attr);
  46. assert(oldValue.isCustomType());
  47. bs->writeVar(curValue, oldValue.customVar());
  48. }
  49. else
  50. bs->writeVar(curValue);
  51. }
  52. }
  53. static BaseObject* getObjFromRef(ObjRef r, bool alsoGameSettings = false) {
  54. switch(r.classId) {
  55. case LuaID<Game>::value: return &game;
  56. case LuaID<CWorm>::value: return game.wormById(r.objId, false);
  57. case LuaID<Settings>::value: if(alsoGameSettings) { assert(game.isServer()); return &gameSettings; }
  58. default: break;
  59. }
  60. return NULL;
  61. }
  62. static bool attrBelongsToClass(const ClassInfo* classInfo, const AttrDesc* attrDesc) {
  63. return classInfo->isTypeOf(attrDesc->objTypeId);
  64. }
  65. // This only make sense as server.
  66. // See also ObjRef::ownThis
  67. static bool ownObject(CServerConnection* source, ObjRef o) {
  68. assert(game.isServer());
  69. // This is very custom right now and need to be made somewhat more general.
  70. if(o.classId == LuaID<CWorm>::value) {
  71. return source->OwnsWorm(o.objId);
  72. }
  73. return false;
  74. }
  75. void GameStateUpdates::handleFromBs(CBytestream* bs, CServerConnection* source) {
  76. if(game.isServer()) {
  77. assert(source != NULL);
  78. if(source->isLocalClient()) {
  79. // This shouldn't happen. It means sth in our server connection list is messed up.
  80. errors << "GameStateUpdates::handleFromBs as server from local client" << endl;
  81. bs->SkipAll();
  82. return;
  83. }
  84. }
  85. else
  86. assert(source == NULL);
  87. uint16_t creationsNum = bs->readInt16();
  88. for(uint16_t i = 0; i < creationsNum; ++i) {
  89. ObjRef r;
  90. r.readFromBs(bs);
  91. //BaseObject* o = getClassInfo(r.classId)->createInstance();
  92. //o->thisRef.classId = r.classId;
  93. //o->thisRef.objId = r.objId;
  94. // we only handle/support CWorm objects for now...
  95. if(game.isServer()) {
  96. errors << "GameStateUpdates::handleFromBs: got obj creation as server: " << r.description() << endl;
  97. bs->SkipAll();
  98. return;
  99. }
  100. if(r.classId != LuaID<CWorm>::value) {
  101. errors << "GameStateUpdates::handleFromBs: obj-creation: invalid class: " << r.description() << endl;
  102. bs->SkipAll();
  103. return;
  104. }
  105. if(game.wormById(r.objId, false) != NULL) {
  106. errors << "GameStateUpdates::handleFromBs: worm-creation: worm " << r.objId << " already exists" << endl;
  107. bs->SkipAll();
  108. return;
  109. }
  110. game.createNewWorm(r.objId, false, NULL, Version());
  111. if(source) {
  112. if(source->gameState->haveObject(r)) {
  113. // it should not have the object at this point. however, the client might be fucked up
  114. errors << "GameStateUpdate from client: add object " << r.description() << " which it should have had" << endl;
  115. }
  116. else
  117. source->gameState->addObject(r);
  118. }
  119. }
  120. uint16_t deletionsNum = bs->readInt16();
  121. for(uint16_t i = 0; i < deletionsNum; ++i) {
  122. ObjRef r;
  123. r.readFromBs(bs);
  124. // we only handle/support CWorm objects for now...
  125. if(game.isServer()) {
  126. errors << "GameStateUpdates::handleFromBs: got obj deletion as server: " << r.description() << endl;
  127. bs->SkipAll();
  128. return;
  129. }
  130. if(r.classId != LuaID<CWorm>::value) {
  131. errors << "GameStateUpdates::handleFromBs: obj-deletion: invalid class: " << r.description() << endl;
  132. bs->SkipAll();
  133. return;
  134. }
  135. CWorm* w = game.wormById(r.objId, false);
  136. if(!w) {
  137. errors << "GameStateUpdates::handleFromBs: obj-deletion: worm " << r.objId << " does not exist" << endl;
  138. bs->SkipAll();
  139. return;
  140. }
  141. game.removeWorm(w);
  142. if(source) {
  143. if(!source->gameState->haveObject(r)) {
  144. // it should always have the object at this point. however, the client might be fucked up
  145. errors << "GameStateUpdate from client: remove obj " << r.description() << " which it should not have had" << endl;
  146. source->gameState->addObject(r);
  147. }
  148. source->gameState->removeObject(r);
  149. }
  150. }
  151. uint32_t attrUpdatesNum = bs->readInt(4);
  152. for(uint32_t i = 0; i < attrUpdatesNum; ++i) {
  153. ObjAttrRef r;
  154. r.readFromBs(bs);
  155. const AttrDesc* attrDesc = r.attr.getAttrDesc();
  156. if(attrDesc == NULL) {
  157. errors << "GameStateUpdates::handleFromBs: AttrDesc for update not found" << endl;
  158. bs->SkipAll();
  159. return;
  160. }
  161. const ClassInfo* classInfo = getClassInfo(r.obj.classId);
  162. if(classInfo == NULL) {
  163. errors << "GameStateUpdates::handleFromBs: class " << r.obj.classId << " for obj-update unknown" << endl;
  164. bs->SkipAll();
  165. return;
  166. }
  167. if(!attrBelongsToClass(classInfo, attrDesc)) {
  168. errors << "GameStateUpdates::handleFromBs: attr " << attrDesc->description() << " does not belong to class " << r.obj.classId << " for obj-update" << endl;
  169. bs->SkipAll();
  170. return;
  171. }
  172. // see GameStateUpdates::diffFromStateToCurrent for the other side
  173. if(attrDesc->serverside) {
  174. if(game.isServer()) {
  175. errors << "GameStateUpdates::handleFromBs: got serverside attr update as server: " << r.description() << endl;
  176. bs->SkipAll();
  177. return;
  178. }
  179. }
  180. else { // client-side attr
  181. if(game.isServer() && !ownObject(source, r.obj)) {
  182. errors << "GameStateUpdates::handleFromBs: got update from not-authorized client " << source->debugName() << ": " << r.description() << endl;
  183. bs->SkipAll();
  184. return;
  185. }
  186. }
  187. // for now, this is somewhat specific to the only types we support
  188. if(r.obj.classId == LuaID<Settings>::value) {
  189. if(game.isServer()) {
  190. errors << "GameStateUpdates::handleFromBs: got settings update as server" << endl;
  191. bs->SkipAll();
  192. return;
  193. }
  194. if(!Settings::getAttrDescs().belongsToUs(attrDesc)) {
  195. errors << "GameStateUpdates::handleFromBs: settings update AttrDesc " << attrDesc->description() << " is not a setting attr" << endl;
  196. bs->SkipAll();
  197. return;
  198. }
  199. // Somewhat hacky right now. We don't really manipulate gameSettings.
  200. ::pushObjAttrUpdate(gameSettings, attrDesc);
  201. FeatureIndex fIndex = Settings::getAttrDescs().getIndex(attrDesc);
  202. bs->readVar(cClient->getGameLobby().write(fIndex));
  203. }
  204. else {
  205. BaseObject* o = getObjFromRef(r.obj);
  206. if(o == NULL) {
  207. errors << "GameStateUpdates::handleFromBs: object for attr update not found: " << r.description() << endl;
  208. bs->SkipAll();
  209. return;
  210. }
  211. if(o->thisRef != r.obj) {
  212. errors << "GameStateUpdates::handleFromBs: object-ref for attr update invalid: " << r.description() << endl;
  213. bs->SkipAll();
  214. return;
  215. }
  216. if(attrDesc->attrType == SVT_CustomWeakRefToStatic) {
  217. CustomVar* v = (CustomVar*)attrDesc->getValuePtr(o);
  218. ::pushObjAttrUpdate(*o, attrDesc);
  219. ScriptVar_t scriptVarRef(v->thisRef.obj);
  220. bs->readVar(scriptVarRef);
  221. assert(scriptVarRef.type == SVT_CustomWeakRefToStatic);
  222. //if(attrDesc->attrName == "weaponSlots")
  223. //notes << "game state update: <" << r.obj.description() << "> " << attrDesc->attrName << " to " << scriptVarRef.toString() << endl;
  224. }
  225. else {
  226. ScriptVar_t v;
  227. bs->readVar(v);
  228. if(attrDesc == game.state.attrDesc()) {
  229. if((int)v < Game::S_Lobby) {
  230. notes << "GameStateUpdates: server changed to " << Game::StateAsStr(v) << ", we wait for drop/leaving package" << endl;
  231. //v = Game::S_Inactive;
  232. continue;
  233. }
  234. }
  235. attrDesc->set(o, v);
  236. }
  237. }
  238. /*if(attrDesc->attrName != "serverFrame")
  239. notes << "game state update: <" << r.obj.description() << "> " << attrDesc->attrName << " to " << v.toString() << endl;*/
  240. if(source) {
  241. if(!source->gameState->haveObject(r.obj)) {
  242. // it should always have the object at this point. however, the client might be fucked up
  243. errors << "GameStateUpdate from client: attrib update " << r.description() << " about object which it should not have had" << endl;
  244. source->gameState->addObject(r.obj);
  245. }
  246. BaseObject* o = getObjFromRef(r.obj, true);
  247. assert(o);
  248. source->gameState->setObjAttr(r, r.attr.getAttrDesc()->get(o));
  249. }
  250. }
  251. }
  252. void GameStateUpdates::pushObjAttrUpdate(ObjAttrRef a) {
  253. objs.insert(a);
  254. }
  255. void GameStateUpdates::pushObjCreation(ObjRef o) {
  256. objDeletions.erase(o);
  257. objCreations.insert(o);
  258. }
  259. void GameStateUpdates::pushObjDeletion(ObjRef o) {
  260. Objs::iterator itStart = objs.lower_bound(ObjAttrRef::LowerLimit(o));
  261. Objs::iterator itEnd = objs.upper_bound(ObjAttrRef::UpperLimit(o));
  262. objs.erase(itStart, itEnd);
  263. objCreations.erase(o);
  264. objDeletions.insert(o);
  265. }
  266. void GameStateUpdates::reset() {
  267. objs.clear();
  268. objCreations.clear();
  269. objDeletions.clear();
  270. }
  271. void GameStateUpdates::diffFromStateToCurrent(const GameState& s) {
  272. reset();
  273. foreach(o, game.gameStateUpdates->objCreations) {
  274. if(game.isClient()) continue; // none-at-all right now... worm-creation is handled independently atm
  275. if(!o->obj) continue;
  276. if(!o->obj.get()->weOwnThis()) continue;
  277. if(!s.haveObject(*o))
  278. pushObjCreation(*o);
  279. }
  280. foreach(u, game.gameStateUpdates->objs) {
  281. BaseObject* obj = u->obj.obj.get();
  282. if(!obj) continue;
  283. const AttrDesc* attrDesc = u->attr.getAttrDesc();
  284. assert(attrDesc != NULL);
  285. if(!attrDesc->shouldUpdate(obj)) continue;
  286. attrDesc->getAttrExt(obj).S2CupdateNeeded = false;
  287. ScriptVar_t curValue = u->get();
  288. ScriptVar_t stateValue = attrDesc->defaultValue;
  289. if(s.haveObject(u->obj))
  290. stateValue = s.getValue(*u);
  291. if(curValue == stateValue) continue;
  292. /*if(attrDesc->attrName != "serverFrame")
  293. notes << "send update " << u->description() << ": " << stateValue.toString() << " -> " << curValue.toString() << endl;*/
  294. pushObjAttrUpdate(*u);
  295. }
  296. foreach(o, game.gameStateUpdates->objDeletions) {
  297. if(game.isClient()) continue; // see obj-creations
  298. if(!o->obj) continue;
  299. if(!o->obj.get()->weOwnThis()) continue;
  300. if(s.haveObject(*o))
  301. pushObjDeletion(*o);
  302. }
  303. }
  304. static ObjectState& GameState_registerObj(GameState& s, BaseObject* obj) {
  305. return s.objs[obj->thisRef] = ObjectState(obj);
  306. }
  307. GameState::GameState() {
  308. // register singletons which are always there
  309. GameState_registerObj(*this, &game);
  310. GameState_registerObj(*this, &gameSettings);
  311. }
  312. GameState GameState::Current() {
  313. GameState s;
  314. s.updateToCurrent();
  315. return s;
  316. }
  317. void GameState::reset() {
  318. *this = GameState();
  319. }
  320. void realCopyVar(ScriptVar_t& var);
  321. void GameState::updateToCurrent() {
  322. reset();
  323. foreach(o, game.gameStateUpdates->objCreations) {
  324. addObject(*o);
  325. }
  326. foreach(u, game.gameStateUpdates->objs) {
  327. ObjAttrRef r = *u;
  328. setObjAttr(r, r.get());
  329. }
  330. }
  331. void GameState::addObject(ObjRef o) {
  332. assert(!haveObject(o));
  333. objs[o] = ObjectState(o);
  334. }
  335. void GameState::removeObject(ObjRef o) {
  336. assert(haveObject(o));
  337. objs.erase(o);
  338. }
  339. void GameState::setObjAttr(ObjAttrRef r, ScriptVar_t value) {
  340. Objs::iterator it = objs.find(r.obj);
  341. assert(it != objs.end());
  342. ObjectState& s = it->second;
  343. assert(s.obj == it->first);
  344. s.attribs[r.attr].value = value;
  345. realCopyVar(s.attribs[r.attr].value);
  346. }
  347. bool GameState::haveObject(ObjRef o) const {
  348. Objs::const_iterator it = objs.find(o);
  349. return it != objs.end();
  350. }
  351. ScriptVar_t GameState::getValue(ObjAttrRef a) const {
  352. Objs::const_iterator it = objs.find(a.obj);
  353. assert(it != objs.end());
  354. assert(it->second.obj == a.obj);
  355. return it->second.getValue(a.attr);
  356. }