FactoryObject GameFactory::GetObject(NetworkID id) { Reference* reference; unsigned char type; cs.StartSession(); reference = Network::Manager()->GET_OBJECT_FROM_ID<Reference*>(id); if (reference) type = GetShared(reference)->second; cs.EndSession(); if (!reference) throw VaultException("Unknown object with NetworkID %llu", id); return FactoryObject(reference, type); }
unsigned int GameFactory::LookupRefID(NetworkID id) { Reference* reference; unsigned int refID; cs.StartSession(); reference = Network::Manager()->GET_OBJECT_FROM_ID<Reference*>(id); if (reference) refID = reference->GetReference(); cs.EndSession(); if (!reference) throw VaultException("Unknown object with NetworkID %llu", id); return refID; }
Lockable* Lockable::Retrieve(unsigned int key) { Lockable* locked; cs.StartSession(); try { locked = keymap.at(key)->Unlock(key); } catch (...) { cs.EndSession(); throw VaultException("Key %08X did not unlock anything", key); } cs.EndSession(); return locked; }
unsigned int Lockable::NextKey() { unsigned int next_key; unsigned int temp_key = key; while (keymap.find(temp_key) != keymap.end()) { if (temp_key == UINT_MAX) temp_key = 0x01; else ++temp_key; if (temp_key == key) throw VaultException("Lockable class ran out of keys"); } next_key = temp_key; key = (temp_key == UINT_MAX ? (0x01) : (temp_key + 0x01)); return next_key; }
unsigned int GameFactory::LookupRefID(NetworkID id) { unsigned int refID; cs.StartSession(); try { Reference* reference = Network::Manager()->GET_OBJECT_FROM_ID<Reference*>(id); refID = (reference != NULL ? reference->GetReference() : throw VaultException("Unknown object with NetworkID %lld", id)); } catch (...) { cs.EndSession(); throw; } cs.EndSession(); return refID; }
weak_ptr<Lockable> Lockable::Poll(unsigned int key) { weak_ptr<Lockable> shared; cs.StartSession(); try { shared = sharemap.at(key); keymap.erase(key); sharemap.erase(key); } catch (...) { cs.EndSession(); throw VaultException("Key %08X did not share anything", key); } cs.EndSession(); return shared; }
vector<FactoryObject> GameFactory::GetMultiple(const vector<unsigned int>& objects) { vector<FactoryObject> result(objects.size()); multimap<Reference*, unsigned int> sort; cs.StartSession(); try { ReferenceList::iterator it; unsigned int i = 0; for (const NetworkID& id : objects) { for (it = instances.begin(); it != instances.end() && it->first->GetReference() != id; ++it); Reference* reference = (it != instances.end() ? it->first : NULL); if (!reference) throw VaultException("Unknown object with reference %08X", id); sort.insert(pair<Reference*, unsigned int>(reference, i)); ++i; } } catch (...) { cs.EndSession(); throw; } cs.EndSession(); for (const pair<Reference*, unsigned int>& reference : sort) result[reference.second] = FactoryObject(reference.first); return result; }
vector<FactoryObject> GameFactory::GetMultiple(const vector<unsigned int>& objects) { vector<FactoryObject> result(objects.size()); multimap<ReferenceList::value_type, unsigned int> sort; cs.StartSession(); try { ReferenceList::iterator it; unsigned int i = 0; for (const auto& refID : objects) { for (it = instances.begin(); it != instances.end() && it->first->GetReference() != refID; ++it); if (it == instances.end()) throw VaultException("Unknown object with reference %08X", refID); // emplace sort.insert(make_pair(*it, i)); ++i; } } catch (...) { cs.EndSession(); throw; } cs.EndSession(); for (const auto& reference : sort) result[reference.second] = FactoryObject(reference.first.first.get(), reference.first.second); return result; }
Exterior::Exterior(const string& table, sqlite3_stmt* stmt) { if (sqlite3_column_count(stmt) != 5) throw VaultException("Malformed input database (exteriors): %s", table.c_str()); unsigned int dlc = static_cast<unsigned int>(sqlite3_column_int(stmt, 4)); // if DLC enabled dlc <<= 24; baseID = static_cast<unsigned int>(sqlite3_column_int(stmt, 0)); x = sqlite3_column_int(stmt, 1); y = sqlite3_column_int(stmt, 2); world = static_cast<unsigned int>(sqlite3_column_int(stmt, 3)); if (world & 0xFF000000) { world &= 0x00FFFFFF; world |= dlc; } if (baseID & 0xFF000000) { baseID &= 0x00FFFFFF; baseID |= dlc; } else { cells.erase(baseID); worlds[world].erase(remove_if(worlds[world].begin(), worlds[world].end(), [=](const Exterior* cell) { return cell->GetBase() == baseID; }), worlds[world].end()); } cells.emplace(baseID, this); worlds[world].emplace_back(this); //All exteriors are also CELLs //const Record& record = Record::Lookup(baseID, "CELL"); }
NetworkID GameFactory::LookupNetworkID(unsigned int refID) { NetworkID id; cs.StartSession(); try { ReferenceList::iterator it; for (it = instances.begin(); it != instances.end() && it->first->GetReference() != refID; ++it); id = (it != instances.end() ? it->first->GetNetworkID() : throw VaultException("Unknown object with reference %08X", refID)); } catch (...) { cs.EndSession(); throw; } cs.EndSession(); return id; }
vector<FactoryObject> GameFactory::GetMultiple(const vector<NetworkID>& objects) { vector<FactoryObject> result(objects.size()); multimap<ReferenceList::value_type, unsigned int> sort; cs.StartSession(); try { unsigned int i = 0; for (const auto& id : objects) { Reference* reference = Network::Manager()->GET_OBJECT_FROM_ID<Reference*>(id); if (!reference) throw VaultException("Unknown object with NetworkID %llu", id); // emplace sort.insert(make_pair(*GetShared(reference), i)); ++i; } } catch (...) { cs.EndSession(); throw; } cs.EndSession(); for (const auto& reference : sort) result[reference.second] = FactoryObject(reference.first.first.get(), reference.first.second); return result; }
vector<FactoryObject> GameFactory::GetMultiple(const vector<NetworkID>& objects) { vector<FactoryObject> result(objects.size()); multimap<Reference*, unsigned int> sort; cs.StartSession(); try { unsigned int i = 0; for (const NetworkID& id : objects) { Reference* reference = Network::Manager()->GET_OBJECT_FROM_ID<Reference*>(id); if (!reference) throw VaultException("Unknown object with NetworkID %lld", id); sort.insert(pair<Reference*, unsigned int>(reference, i)); ++i; } } catch (...) { cs.EndSession(); throw; } cs.EndSession(); for (const pair<Reference*, unsigned int>& reference : sort) result[reference.second] = FactoryObject(reference.first); return result; }
NetworkResponse NetworkClient::ProcessEvent(unsigned char id) { switch (id) { case ID_EVENT_CLIENT_ERROR: case ID_EVENT_INTERFACE_LOST: return NetworkResponse{Network::CreateResponse( PacketFactory::CreatePacket(ID_GAME_END, ID_REASON_ERROR), HIGH_PRIORITY, RELIABLE_ORDERED, CHANNEL_GAME, Game::server) }; case ID_EVENT_GAME_STARTED: Network::ToggleDequeue(false); return NetworkResponse{Network::CreateResponse( PacketFactory::CreatePacket(ID_GAME_LOAD), HIGH_PRIORITY, RELIABLE_ORDERED, CHANNEL_GAME, Game::server) }; case ID_EVENT_GAME_LOADED: { Network::ToggleDequeue(true); FactoryObject reference = GameFactory::GetObject(PLAYER_REFERENCE); Player* self = vaultcast<Player>(reference); return NetworkResponse{Network::CreateResponse( self->toPacket(), HIGH_PRIORITY, RELIABLE_ORDERED, CHANNEL_GAME, Game::server) }; } default: throw VaultException("Unhandled event type %d", id); } }
DWORD WINAPI Interface::CommandThreadReceive( LPVOID data ) { try { pipeClient->SetPipeAttributes( "BethesdaClient", PIPE_LENGTH ); pipeClient->CreateServer(); pipeClient->ConnectToServer(); pipeServer->SetPipeAttributes( "BethesdaServer", PIPE_LENGTH ); while ( !pipeServer->ConnectToServer() && !endThread ); char buffer[PIPE_LENGTH]; char code; if ( !endThread ) { do { ZeroMemory( buffer, sizeof( buffer ) ); pipeClient->Receive( buffer ); code = buffer[0]; char* content = buffer + 1; if ( code == PIPE_OP_RETURN || code == PIPE_OP_RETURN_BIG ) { vector<CommandResult> result = API::Translate( buffer ); vector<CommandResult>::iterator it; for ( it = result.begin(); it != result.end(); ++it ) resultHandler( it->first.first.first, it->first.first.second, it->first.second, it->second ); } else if ( code == PIPE_SYS_WAKEUP ) { wakeup = true; #ifdef VAULTMP_DEBUG if ( debug != NULL ) debug->Print( "vaultmp process waked up (game patched)", true ); #endif } else if ( code ) throw VaultException( "Unknown pipe code identifier %02X", code ); if ( lookupProgramID( ( char* ) data ) == 0 ) { endThread = true; #ifdef VAULTMP_DEBUG if ( debug != NULL ) debug->Print( "Game process missing, shutting down", true ); #endif } } while ( code != PIPE_ERROR_CLOSE && !endThread ); } // kill game process if running } catch ( std::exception& e ) { try { VaultException& vaulterror = dynamic_cast<VaultException&>( e ); vaulterror.Message(); } catch ( std::bad_cast& no_vaulterror ) { VaultException vaulterror( e.what() ); vaulterror.Message(); } #ifdef VAULTMP_DEBUG if ( debug != NULL ) debug->Print( "Receive thread is going to terminate (ERROR)", true ); #endif endThread = true; return ( ( DWORD ) data ); } #ifdef VAULTMP_DEBUG if ( debug != NULL ) debug->Print( "Receive thread is going to terminate", true ); #endif endThread = true; return ( ( DWORD ) data ); }
int main(int argc, char* argv[]) { #ifdef VAULTMP_DEBUG #ifdef __WIN32__ if (LoadLibrary("exchndl.dll") == NULL) return 0; #else system("ulimit -c unlimited"); #endif #endif #ifdef __WIN32__ printf("Vault-Tec dedicated server %s (Windows)\n----------------------------------------------------------\n", DEDICATED_VERSION); #else printf("Vault-Tec dedicated server %s (Unix)\n----------------------------------------------------------\n", DEDICATED_VERSION); #endif unsigned char game; int port; int players; int fileslots; bool query; bool files; const char* announce; const char* scripts; const char* mods; const char* savegame; dictionary* config = iniparser_load(argc > 1 ? argv[1] : "vaultserver.ini"); const char* game_str = iniparser_getstring(config, "general:game", "fallout3"); if (stricmp(game_str, "newvegas") == 0) game = NEWVEGAS; else game = FALLOUT3; port = iniparser_getint(config, "general:port", RAKNET_STANDARD_PORT); players = iniparser_getint(config, "general:players", RAKNET_STANDARD_CONNECTIONS); query = (bool) iniparser_getboolean(config, "general:query", 1); files = (bool) iniparser_getboolean(config, "general:fileserve", 0); fileslots = iniparser_getint(config, "general:fileslots", 8); announce = iniparser_getstring(config, "general:master", "vaultmp.com"); savegame = iniparser_getstring(config, "general:save", "default.fos"); scripts = iniparser_getstring(config, "scripts:scripts", ""); mods = iniparser_getstring(config, "mods:mods", ""); ServerEntry* self = new ServerEntry(game); self->SetServerRule("version", DEDICATED_VERSION); Dedicated::SetServerEntry(self); char base[MAX_PATH]; _getcwd(base, sizeof(base)); try { putenv(PWNFILES_PATH); char _scripts[strlen(scripts) + 1]; snprintf(_scripts, sizeof(_scripts), "%s", scripts); Script::LoadScripts(_scripts, base); } catch (std::exception& e) { try { VaultException& vaulterror = dynamic_cast<VaultException&>(e); vaulterror.Console(); } catch (std::bad_cast& no_vaulterror) { VaultException vaulterror(e.what()); vaulterror.Console(); } } try { char file[MAX_PATH]; snprintf(file, sizeof(file), "%s/%s/%s", base, SAVEGAME_PATH, savegame); unsigned int crc; if (!Utils::crc32file(file, &crc)) throw VaultException("Could not find savegame %s in folder %s", savegame, SAVEGAME_PATH); Dedicated::SetSavegame(Savegame(string(savegame), crc)); char buf[strlen(mods) + 1]; strcpy(buf, mods); char* token = strtok(buf, ","); ModList modfiles; while (token != NULL) { snprintf(file, sizeof(file), "%s/%s/%s", base, MODFILES_PATH, token); if (!Utils::crc32file(file, &crc)) throw VaultException("Could not find modfile %s in folder %s", token, MODFILES_PATH); modfiles.push_back(pair<string, unsigned int>(string(token), crc)); token = strtok(NULL, ","); } Dedicated::SetModfiles(modfiles); thread hDedicatedThread = Dedicated::InitializeServer(port, players, announce, query, files, fileslots); thread hInputThread = thread(InputThread); hDedicatedThread.join(); if (hInputThread.joinable()) hInputThread.join(); } catch (std::exception& e) { try { VaultException& vaulterror = dynamic_cast<VaultException&>(e); vaulterror.Console(); } catch (std::bad_cast& no_vaulterror) { VaultException vaulterror(e.what()); vaulterror.Console(); } } Script::UnloadScripts(); iniparser_freedict(config); delete self; #ifdef __WIN32__ system("PAUSE"); #endif return 0; }
void Interface::CommandThreadReceive(bool steam) { try { pipeClient->SetPipeAttributes("BethesdaClient", PIPE_LENGTH); pipeClient->CreateServer(); pipeClient->ConnectToServer(); pipeServer->SetPipeAttributes("BethesdaServer", PIPE_LENGTH); while (!pipeServer->ConnectToServer() && !endThread); unsigned char buffer[PIPE_LENGTH]; buffer[0] = steam; pipeClient->Send(buffer); if (!endThread) { unsigned char code; while (!endThread) { ZeroMemory(buffer, sizeof(buffer)); pipeClient->Receive(buffer); code = buffer[0]; if (code == PIPE_OP_RETURN || code == PIPE_OP_RETURN_BIG || code == PIPE_OP_RETURN_RAW) { vector<CommandResult> result = API::Translate(buffer); for (CommandResult& _result : result) { resultHandler(get<0>(_result), get<1>(_result), get<2>(_result), get<3>(_result)); if (endThread) break; } } else if (code == PIPE_SYS_WAKEUP) { wakeup = true; #ifdef VAULTMP_DEBUG if (debug) debug->Print("vaultmp process waked up (game patched)", true); #endif } else if (code == PIPE_ERROR_CLOSE) { if (!endThread) throw VaultException("Error in vaultmp.dll"); } else if (code) throw VaultException("Unknown pipe code identifier %02X", code); else endThread = true; } } } catch (exception& e) { try { VaultException& vaulterror = dynamic_cast<VaultException&>(e); vaulterror.Message(); } catch (bad_cast& no_vaulterror) { VaultException vaulterror(e.what()); vaulterror.Message(); } #ifdef VAULTMP_DEBUG if (debug) debug->Print("Receive thread is going to terminate (ERROR)", true); #endif } endThread = true; }
NetworkResponse NetworkClient::ProcessPacket(Packet* data) { NetworkResponse response; pDefault* packet; switch (data->data[0]) { case ID_CONNECTION_REQUEST_ACCEPTED: { #ifdef VAULTMP_DEBUG debug->PrintFormat("Connection request accepted (%s)", true, data->systemAddress.ToString()); #endif response = Game::Authenticate(Bethesda::password); break; } case ID_DISCONNECTION_NOTIFICATION: { #ifdef VAULTMP_DEBUG debug->PrintFormat("Connection closed (%s)", true, data->systemAddress.ToString()); #endif break; } case ID_INVALID_PASSWORD: throw VaultException("Dedicated server version mismatch.\nPlease download the most recent binaries from www.vaultmp.com"); case ID_NO_FREE_INCOMING_CONNECTIONS: throw VaultException("The server is full"); case ID_CONNECTION_ATTEMPT_FAILED: throw VaultException("Failed to connect to the server"); case ID_CONNECTION_BANNED: throw VaultException("You are banned from the server"); case ID_CONNECTION_LOST: throw VaultException("Lost connection to the server"); case ID_UNCONNECTED_PONG: break; default: { pPacket _packet = PacketFactory::CreatePacket(data->data, data->length); const pDefault* packet = _packet.get(); switch (data->data[0]) { case ID_GAME_MOD: { char modfile[MAX_MOD_FILE + 1]; ZeroMemory(modfile, sizeof(modfile)); unsigned int crc; PacketFactory::Access(packet, modfile, &crc); Bethesda::modfiles.push_back(pair<string, unsigned int>(string(modfile), crc)); break; } case ID_GAME_START: { #ifdef VAULTMP_DEBUG debug->PrintFormat("We were successfully authenticated (%s)", true, data->systemAddress.ToString()); debug->Print("Initiating vaultmp game thread...", true); #endif char savegame[MAX_SAVEGAME_FILE + 1]; ZeroMemory(savegame, sizeof(savegame)); unsigned int crc; PacketFactory::Access(packet, savegame, &crc); Bethesda::savegame = Savegame(string(savegame), crc); Bethesda::Initialize(); Game::LoadGame(Utils::FileOnly(Bethesda::savegame.first.c_str())); Game::LoadEnvironment(); response = NetworkClient::ProcessEvent(ID_EVENT_GAME_STARTED); break; } case ID_GAME_LOAD: { Game::Startup(); response = NetworkClient::ProcessEvent(ID_EVENT_GAME_LOADED); break; } case ID_GAME_END: { unsigned char reason; PacketFactory::Access(packet, &reason); switch (reason) { case ID_REASON_KICK: throw VaultException("You have been kicked from the server"); case ID_REASON_BAN: throw VaultException("You have been banned from the server"); case ID_REASON_ERROR: throw VaultException("The server encountered an internal error"); case ID_REASON_DENIED: throw VaultException("Your authentication has been denied"); case ID_REASON_NONE: break; } break; } case ID_GAME_MESSAGE: { char message[MAX_MESSAGE_LENGTH + 1]; ZeroMemory(message, sizeof(message)); PacketFactory::Access(packet, message); Game::net_UIMessage(message); break; } case ID_GAME_CHAT: { char message[MAX_CHAT_LENGTH + 1]; ZeroMemory(message, sizeof(message)); PacketFactory::Access(packet, message); Game::net_ChatMessage(message); break; } case ID_OBJECT_NEW: { NetworkID id = GameFactory::CreateKnownInstance(ID_OBJECT, packet); FactoryObject reference = GameFactory::GetObject(id); Game::NewObject(reference); break; } case ID_ITEM_NEW: { NetworkID id = GameFactory::CreateKnownInstance(ID_ITEM, packet); FactoryObject reference = GameFactory::GetObject(id); Game::NewItem(reference); break; } case ID_CONTAINER_NEW: { NetworkID id = GameFactory::CreateKnownInstance(ID_CONTAINER, packet); FactoryObject reference = GameFactory::GetObject(id); Game::NewContainer(reference); break; } case ID_ACTOR_NEW: { NetworkID id = GameFactory::CreateKnownInstance(ID_ACTOR, packet); FactoryObject reference = GameFactory::GetObject(id); Game::NewActor(reference); break; } case ID_PLAYER_NEW: { NetworkID id = GameFactory::CreateKnownInstance(ID_PLAYER, packet); FactoryObject reference = GameFactory::GetObject(id); Game::NewPlayer(reference); break; } case ID_OBJECT_REMOVE: { NetworkID id; PacketFactory::Access(packet, &id); FactoryObject reference = GameFactory::GetObject(id); Game::Delete(reference); break; } case ID_OBJECT_UPDATE: case ID_CONTAINER_UPDATE: case ID_ACTOR_UPDATE: case ID_PLAYER_UPDATE: { switch (data->data[1]) { case ID_UPDATE_POS: { NetworkID id; double X, Y, Z; PacketFactory::Access(packet, &id, &X, &Y, &Z); FactoryObject reference = GameFactory::GetObject(id); Game::net_SetPos(reference, X, Y, Z); break; } case ID_UPDATE_ANGLE: { NetworkID id; unsigned char axis; double value; PacketFactory::Access(packet, &id, &axis, &value); FactoryObject reference = GameFactory::GetObject(id); Game::net_SetAngle(reference, axis, value); break; } case ID_UPDATE_CELL: { NetworkID id; unsigned int cell; PacketFactory::Access(packet, &id, &cell); vector<FactoryObject> reference = GameFactory::GetMultiple(vector<unsigned int> {GameFactory::LookupRefID(id), PLAYER_REFERENCE}); Game::net_SetCell(reference, cell); break; } case ID_UPDATE_CONTAINER: { NetworkID id; ContainerDiff diff; PacketFactory::Access(packet, &id, &diff); FactoryObject reference = GameFactory::GetObject(id); Game::net_ContainerUpdate(reference, diff); break; } case ID_UPDATE_VALUE: { NetworkID id; bool base; unsigned char index; double value; PacketFactory::Access(packet, &id, &base, &index, &value); FactoryObject reference = GameFactory::GetObject(id); Game::net_SetActorValue(reference, base, index, value); break; } case ID_UPDATE_STATE: { NetworkID id; unsigned char index; unsigned char moving; bool alerted; bool sneaking; PacketFactory::Access(packet, &id, &index, &moving, &alerted, &sneaking); FactoryObject reference = GameFactory::GetObject(id); Game::net_SetActorState(reference, index, moving, alerted, sneaking); break; } case ID_UPDATE_DEAD: { NetworkID id; bool dead; PacketFactory::Access(packet, &id, &dead); FactoryObject reference = GameFactory::GetObject(id); Game::net_SetActorDead(reference, dead); break; } default: throw VaultException("Unhandled object update packet type %d", (int) data->data[1]); } break; } default: throw VaultException("Unhandled packet type %d", (int) data->data[0]); } } } return response; }