/* Starshatter: The Open Source Project Copyright (c) 2021-2022, Starshatter: The Open Source Project Contributors Copyright (c) 2011-2012, Starshatter OpenSource Distribution Contributors Copyright (c) 1997-2006, Destroyer Studios LLC. AUTHOR: John DiCamillo */ #include #include "StarServer.h" #include "Campaign.h" #include "CombatRoster.h" #include "Galaxy.h" #include "Mission.h" #include "Sim.h" #include "SimEvent.h" #include "Ship.h" #include "Contact.h" #include "QuantumDrive.h" #include "Power.h" #include "SystemDesign.h" #include "WeaponDesign.h" #include "Shot.h" #include "Drive.h" #include "Explosion.h" #include "FlightDeck.h" #include "RadioMessage.h" #include "RadioTraffic.h" #include "Random.h" #include "ModConfig.h" #include "NetLayer.h" #include "NetGame.h" #include "NetHost.h" #include "NetServer.h" #include "HttpServer.h" #include "HttpServletExec.h" #include "NetAdminServer.h" #include "NetLobbyServer.h" #include "NetServerConfig.h" #include "Token.h" #include "MachineInfo.h" #include "Game.h" #include "Clock.h" #include "ContentBundle.h" #include "Keyboard.h" #include "Mouse.h" #include "EventDispatch.h" #include "MultiController.h" #include "DataLoader.h" #include "ParseUtil.h" #include "VersionInfo.h" // +--------------------------------------------------------------------+ StarServer* StarServer::instance {nullptr}; static Mission* current_mission = 0; static double time_til_change = 0; static bool exit_latch = true; // +--------------------------------------------------------------------+ StarServer::StarServer() : world {nullptr}, loader(0), time_mark(0), minutes(0), admin_server(0), lobby_server(0) { if (instance != nullptr) throw "StarServer may have only one instance"; instance = this; server = true; show_mouse = true; DataLoader::Initialize(); loader = DataLoader::GetLoader(); int loadstat = loader->EnableDatafile("shatter.dat"); if (loadstat != DataLoader::DATAFILE_OK) { const char* err_msg = loadstat == DataLoader::DATAFILE_INVALID ? "The file 'shatter.dat' appears to have been damaged. Please re-install Starshatter." : "Starshatter cannot open the file 'shatter.dat'. Please re-install Starshatter."; ::Print(err_msg); ::Print("\n\nFATAL ERROR: EXIT."); exit(-1); } if (loader->FindFile("start.dat")) loader->EnableDatafile("start.dat"); // no images or sounds in server mode: loader->EnableMedia(false); } StarServer::~StarServer() { delete admin_server; delete lobby_server; admin_server = 0; lobby_server = 0; // delete all the ships and stuff // BEFORE getting rid of the system // and weapons catalogs! if (world) delete world; world = nullptr; Drive::Close(); Explosion::Close(); FlightDeck::Close(); Campaign::Close(); CombatRoster::Close(); Galaxy::Close(); RadioTraffic::Close(); Ship::Close(); WeaponDesign::Close(); SystemDesign::Close(); DataLoader::Close(); NetServerConfig::Close(); ModConfig::Close(); server = false; if (instance == this) instance = nullptr; } // +--------------------------------------------------------------------+ bool StarServer::Init(HINSTANCE hi, HINSTANCE hpi, LPSTR cmdline, int nCmdShow) { if (loader) loader->UseFileSystem(false); return Game::Init(hi, hpi, cmdline, nCmdShow); } // +--------------------------------------------------------------------+ bool StarServer::InitGame() { if (!Game::InitGame()) return false; RandomInit(); ModConfig::Initialize(); NetServerConfig::Initialize(); SystemDesign::Initialize("sys.def"); WeaponDesign::Initialize("wep.def"); Ship::Initialize(); Galaxy::Initialize(); CombatRoster::Initialize(); Campaign::Initialize(); Drive::Initialize(); Explosion::Initialize(); FlightDeck::Initialize(); Ship::Initialize(); Shot::Initialize(); RadioTraffic::Initialize(); time_mark = Clock::GetInstance()->GameTime(); minutes = 0; NetServerConfig* server_config = NetServerConfig::GetInstance(); if (!server_config) return false; ::Print("\n\n\nStarshatter Server Init\n"); ::Print("-----------------------\n"); ::Print("Server Name: %s\n", (const char*) server_config->Name()); ::Print("Server Type: %d\n", server_config->GetGameType()); if (server_config->GetMission().length() > 0) ::Print("Server Mission: %s\n", (const char*) server_config->GetMission()); ::Print("Lobby Server Port: %d\n", server_config->GetLobbyPort()); ::Print("Admin Server Port: %d\n", server_config->GetAdminPort()); ::Print("-----------------------\n"); NetLobbyServer* nls = new NetLobbyServer; NetAdminServer* nas = NetAdminServer::GetInstance(server_config->GetAdminPort()); nas->SetServerName(server_config->Name()); lobby_server = nls; admin_server = nas; std::cout << "Started server listening on port " << server_config->GetLobbyPort() << std::endl; return true; } // +--------------------------------------------------------------------+ void StarServer::SetGameMode(int m) { if (game_mode == m) return; if (m == LOAD_MODE) { Print(" game_mode = LOAD_MODE\n"); paused = true; } else if (m == PLAY_MODE) { Print(" game_mode = PLAY_MODE\n"); if (!world) { CreateWorld(); InstantiateMission(); } // stand alone server should wait for players to connect // before unpausing the simulation... Clock::GetInstance()->SetTimeCompression(1.0); Pause(true); } else if (m == MENU_MODE) { Print(" game_mode = MENU_MODE\n"); paused = true; Sim* sim = (Sim*) world; if (sim) sim->UnloadMission(); } game_mode = m; } // +--------------------------------------------------------------------+ void StarServer::SetNextMission(const char* script) { if (lobby_server) lobby_server->SetServerMission(script); } // +--------------------------------------------------------------------+ void StarServer::CreateWorld() { RadioTraffic::Initialize(); // create world if (!world) { Sim* sim = new Sim(0); world = sim; Print(" World Created.\n"); } } void StarServer::InstantiateMission() { current_mission = 0; if (Campaign::GetCampaign()) { current_mission = Campaign::GetCampaign()->GetMission(); } Sim* sim = (Sim*) world; if (sim) { sim->UnloadMission(); if (current_mission) { sim->LoadMission(current_mission); sim->ExecMission(); sim->SetTestMode(false); Print(" Mission Instantiated.\n"); std::cout << "Loaded mission: " << current_mission->Name() << std::endl; } else { Print(" *** WARNING: StarServer::InstantiateMission() - no mission selected ***\n"); } } } // +--------------------------------------------------------------------+ void StarServer::UpdateWorld() { Galaxy* galaxy = Galaxy::GetInstance(); if (galaxy) galaxy->ExecFrame(); Campaign* campaign = Campaign::GetCampaign(); if (campaign) campaign->ExecFrame(); if (paused) { if (world) world->ExecFrame(0); } else { Drive::StartFrame(); if (world) world->ExecFrame(Clock::GetInstance()->Delta()); } static DWORD refresh_time = 0; if (Clock::GetInstance()->RealTime() - refresh_time > 1000) { refresh_time = Clock::GetInstance()->RealTime(); } } // +--------------------------------------------------------------------+ void StarServer::GameState() { if (lobby_server) { lobby_server->ExecFrame(); if (lobby_server->GetStatus() == NetServerInfo::PERSISTENT) paused = NetGame::NumPlayers() < 1; } if (game_mode == MENU_MODE) { Sleep(30); } else if (game_mode == LOAD_MODE) { CreateWorld(); InstantiateMission(); SetGameMode(PLAY_MODE); } else if (game_mode == PLAY_MODE) { if (Clock::GetInstance()->GameTime() - time_mark > 60000) { time_mark = Clock::GetInstance()->GameTime(); minutes++; if (minutes > 60) Print(" TIME %2d:%02d:00\n", minutes/60, minutes%60); else Print(" TIME %2d:00\n", minutes); } Sleep(10); } } // +--------------------------------------------------------------------+ DWORD WINAPI StarServerShutdownProc(LPVOID link) { StarServer* stars = (StarServer*) link; Sleep(3000); if (stars) { stars->Exit(); return 0; } return (DWORD) E_POINTER; } DWORD WINAPI StarServerRestartProc(LPVOID link) { StarServer* stars = (StarServer*) link; Sleep(3000); if (stars) { char cmdline[256]; strcpy_s(cmdline, "stars -server"); STARTUPINFO s; ZeroMemory(&s, sizeof(s)); s.cb = sizeof(s); PROCESS_INFORMATION pi; ZeroMemory(&pi, sizeof(pi)); CreateProcess("stars.exe", cmdline, 0, 0, 0, 0, 0, 0, &s, &pi); stars->Exit(); CloseHandle( pi.hProcess ); CloseHandle( pi.hThread ); return 0; } return (DWORD) E_POINTER; } void StarServer::Shutdown(bool restart) { DWORD thread_id = 0; if (restart) CreateThread(0, 4096, StarServerRestartProc, (LPVOID) this, 0, &thread_id); else CreateThread(0, 4096, StarServerShutdownProc, (LPVOID) this, 0, &thread_id); }