From 3c487c5cd69c53d6fea948643c0a76df03516605 Mon Sep 17 00:00:00 2001 From: Aki Date: Fri, 1 Apr 2022 21:23:39 +0200 Subject: Moved Stars45 to StarsEx --- StarsEx/NetGameServer.cpp | 1202 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1202 insertions(+) create mode 100644 StarsEx/NetGameServer.cpp (limited to 'StarsEx/NetGameServer.cpp') diff --git a/StarsEx/NetGameServer.cpp b/StarsEx/NetGameServer.cpp new file mode 100644 index 0000000..ef33a90 --- /dev/null +++ b/StarsEx/NetGameServer.cpp @@ -0,0 +1,1202 @@ +/* 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 + + + OVERVIEW + ======== + Server-Side Network Game Manager class +*/ + +#include "NetGameServer.h" +#include "NetServerConfig.h" +#include "NetLobbyServer.h" +#include "NetPlayer.h" +#include "NetUser.h" +#include "NetMsg.h" +#include "NetData.h" +#include "Ship.h" +#include "ShipDesign.h" +#include "Shield.h" +#include "Sim.h" +#include "SimEvent.h" +#include "Element.h" +#include "HUDView.h" +#include "RadioMessage.h" +#include "RadioView.h" +#include "Instruction.h" +#include "Hangar.h" +#include "FlightDeck.h" +#include "Mission.h" +#include "Text.h" + +#include "NetLayer.h" +#include "NetHost.h" +#include "NetPeer.h" +#include "NetUtil.h" +#include "Game.h" +#include "Clock.h" +#include "ContentBundle.h" +#include "Light.h" + +// +--------------------------------------------------------------------+ + +const int MAX_NET_FPS = 20; +const int MIN_NET_FRAME = 1000 / MAX_NET_FPS; + +// +--------------------------------------------------------------------+ + +NetGameServer::NetGameServer() +{ + Print("Constructing NetGameServer\n"); + + Text hostname = "0.0.0.0"; + WORD server_port = 11101; + + if (NetServerConfig::GetInstance()) { + hostname = NetServerConfig::GetInstance()->Hostname(); + server_port = NetServerConfig::GetInstance()->GetGamePort(); + } + + NetAddr server(hostname.data(), server_port); + + link = new NetLink(server); + + ListIter rgn_iter = sim->GetRegions(); + while (++rgn_iter) { + SimRegion* rgn = rgn_iter.value(); + + ListIter iter = rgn->Ships(); + while (++iter) { + Ship* s = iter.value(); + s->SetObjID(GetNextObjID()); + Observe(s); + ships.append(s); + } + } + + if (local_player) { + Observe(local_player); + objid = local_player->GetObjID(); + } +} + +NetGameServer::~NetGameServer() +{ + ListIter player = players; + while (++player) { + NetPlayer* p = player.value(); + + if (p->GetShip()) + p->GetShip()->SetRespawnCount(0); + + link->SendMessage(p->GetNetID(), NET_GAME_OVER, 0, 0, NetMsg::RELIABLE); + } + + Sleep(500); + + ListIter iter = ships; + while (++iter) { + Ship* s = iter.value(); + s->SetRespawnCount(0); + } + + zombies.destroy(); + ships.clear(); +} + +// +--------------------------------------------------------------------+ + +void +NetGameServer::ExecFrame() +{ + NetGame::ExecFrame(); + CheckSessions(); + + ListIter rgn_iter = sim->GetRegions(); + while (++rgn_iter) { + SimRegion* rgn = rgn_iter.value(); + + ListIter iter = rgn->Ships(); + while (++iter) { + Ship* s = iter.value(); + + if (s->GetObjID() == 0) { + s->SetObjID(GetNextObjID()); + Observe(s); + ships.append(s); + + NetJoinAnnounce join_ann; + join_ann.SetShip(s); + join_ann.SetName("Server A.I. Ship"); + SendData(&join_ann); + } + } + } + + + static DWORD time_mark = 0; + + if (!time_mark) time_mark = Clock::GetInstance()->RealTime(); + else if (Clock::GetInstance()->RealTime() - time_mark > 60000) { + time_mark = Clock::GetInstance()->RealTime(); + + if (link && players.size() > 0) { + Print("Server Stats\n-------------\n"); + Print(" packets sent %d\n", link->GetPacketsSent()); + Print(" packets recv %d\n", link->GetPacketsRecv()); + Print(" bytes sent %d\n", link->GetBytesSent()); + Print(" bytes recv %d\n", link->GetBytesRecv()); + Print(" retries %d\n", link->GetRetries()); + Print(" drops %d\n", link->GetDrops()); + Print(" avg lag %d msec\n", link->GetLag()); + } + } +} + +void +NetGameServer::CheckSessions() +{ + if (!link) + return; + + ListIter iter = players; + while (++iter) { + NetPlayer* player = iter.value(); + NetPeer* peer = link->FindPeer(player->GetNetID()); + + if (peer && (NetLayer::GetUTC() - peer->LastReceiveTime()) > NET_DISCONNECT_TIME) { + // announce drop: + NetPlayer* zombie = iter.removeItem(); + HUDView::Message(ContentBundle::GetInstance()->GetText("NetGameServer.remote-discon").data(), zombie->Name()); + + // tell everyone else: + NetQuitAnnounce quit_ann; + quit_ann.SetObjID(zombie->GetObjID()); + quit_ann.SetDisconnected(true); + SendData(&quit_ann); + + // return remote ship to ship pool: + Ship* s = zombie->GetShip(); + if (s) { + Observe(s); + ships.append(s); + s->SetNetworkControl(0); + zombie->SetShip(0); + + NetJoinAnnounce join_ann; + join_ann.SetShip(s); + join_ann.SetName("Server A.I. Ship"); + SendData(&join_ann); + } + + zombies.append(zombie); + } + } +} + +NetPlayer* +NetGameServer::FindZombieByObjID(DWORD objid) +{ + for (int i = 0; i < zombies.size(); i++) { + NetPlayer* p = zombies[i]; + + if (p->GetObjID() == objid) + return p; + } + + return 0; +} + +// +--------------------------------------------------------------------+ + +void +NetGameServer::DoJoinRequest(NetMsg* msg) +{ + if (!msg) return; + + bool unpause = players.isEmpty(); + + NetJoinRequest join_req; + if (join_req.Unpack(msg->Data())) { + HUDView::Message(ContentBundle::GetInstance()->GetText("NetGameServer::join-request").data(), join_req.GetName(), join_req.GetElement(), join_req.GetIndex()); + + DWORD nid = msg->NetID(); + Text name = join_req.GetName(); + Text serno = join_req.GetSerialNumber(); + Text elem_name = join_req.GetElement(); + int index = join_req.GetIndex(); + Ship* ship = 0; + Sim* sim = Sim::GetSim(); + + if (sim) { + Element* element = sim->FindElement(elem_name); + + if (element) + ship = element->GetShip(index); + } + + if (!ship) { + Print(" JOIN DENIED: could not locate ship for remote player\n"); + return; + } + + if (!ship->GetObjID()) { + Print(" JOIN DENIED: remote player requested ship with objid = 0\n"); + return; + } + + NetLobbyServer* lobby = NetLobbyServer::GetInstance(); + + if (lobby) { + NetUser* user = lobby->FindUserByName(name); + + if (!user) + user = lobby->FindUserByNetID(nid); + + if (!user) { + Print(" JOIN DENIED: remote player '%s' not found in lobby\n", name.data()); + return; + } + + else if (!user->IsAuthOK()) { + Print(" JOIN DENIED: remote player '%s' not authenticated\n", name.data()); + return; + } + } + + NetPlayer* remote_player = FindPlayerByNetID(nid); + if (remote_player && remote_player->GetShip() != ship) { + Print(" disconnecting remote player from ship '%s'\n", ship->Name()); + players.remove(remote_player); + delete remote_player; + remote_player = 0; + } + + if (!remote_player) { + Ignore(ship); + ships.remove(ship); + + remote_player = new NetPlayer(nid); + remote_player->SetName(name); + remote_player->SetSerialNumber(serno); + remote_player->SetObjID(ship->GetObjID()); + remote_player->SetShip(ship); + + HUDView::Message(ContentBundle::GetInstance()->GetText("NetGameServer::join-announce").data()); + Print("remote player name = %s\n", name.data()); + Print(" obj = %d\n", ship->GetObjID()); + Print(" ship = %s\n", ship->Name()); + + remote_player->SetObjID(ship->GetObjID()); + + // tell the new player about the server: + if (local_player) { + NetJoinAnnounce join_ann; + join_ann.SetShip(local_player); + join_ann.SetName(player_name); + link->SendMessage(remote_player->GetNetID(), join_ann.Pack(), NetJoinAnnounce::SIZE, NetMsg::RELIABLE); + } + + // tell the new player about the existing remote players: + ListIter iter = players; + while (++iter) { + Ship* s = iter->GetShip(); + + if (s) { + NetJoinAnnounce join_ann; + join_ann.SetShip(s); + join_ann.SetName(iter->Name()); + join_ann.SetObjID(iter->GetObjID()); + link->SendMessage(remote_player->GetNetID(), join_ann.Pack(), NetJoinAnnounce::SIZE, NetMsg::RELIABLE); + } + } + + // tell the new player about the A.I. controlled ships: + ListIter ai_iter = ships; + while (++ai_iter) { + Ship* s = ai_iter.value(); + if (s != local_player) { + NetJoinAnnounce join_ann; + join_ann.SetShip(s); + join_ann.SetName("Server A.I. Ship"); + link->SendMessage(remote_player->GetNetID(), join_ann.Pack(), NetJoinAnnounce::SIZE, NetMsg::RELIABLE); + } + } + + // make the new player an "existing" remote player: + players.append(remote_player); + + // tell existing players about the new player: + // NOTE, this also provides the net id to the new player! + iter.reset(); + while (++iter) { + Ship* s = remote_player->GetShip(); + + NetJoinAnnounce join_ann; + join_ann.SetShip(s); + join_ann.SetName(remote_player->Name()); + join_ann.SetObjID(remote_player->GetObjID()); + link->SendMessage(iter->GetNetID(), join_ann.Pack(), NetJoinAnnounce::SIZE, NetMsg::RELIABLE); + } + + if (unpause) { + auto game = Game::GetInstance(); + if (game) + game->Pause(false); + } + } + } +} + +void +NetGameServer::DoJoinAnnounce(NetMsg* msg) +{ + if (!msg) return; + + NetJoinAnnounce join_ann; + if (join_ann.Unpack(msg->Data())) { + Print("Server received Join Announce from '%s'\n", join_ann.GetName()); + } +} + +void +NetGameServer::DoQuitRequest(NetMsg* msg) +{ + if (!msg) return; + + NetPlayer* player = FindPlayerByNetID(msg->NetID()); + + if (player) { + NetPlayer* zombie = players.remove(player); + HUDView::Message(ContentBundle::GetInstance()->GetText("NetGameServer.remote-quit").data(), zombie->Name()); + + // tell everyone else: + NetQuitAnnounce quit_ann; + quit_ann.SetObjID(zombie->GetObjID()); + SendData(&quit_ann); + + // return remote ship to ship pool: + Ship* s = zombie->GetShip(); + if (s) { + Observe(s); + ships.append(s); + s->SetNetworkControl(0); + zombie->SetShip(0); + + NetJoinAnnounce join_ann; + join_ann.SetShip(s); + join_ann.SetName("Server A.I. Ship"); + SendData(&join_ann); + } + + zombies.append(zombie); + } + else { + Print("Quit Request from unknown player NetID: %08X\n", msg->NetID()); + } +} + +void +NetGameServer::DoQuitAnnounce(NetMsg* msg) +{ + if (!msg) return; + Print("Server received Quit Announce from NetID: %08x\n", msg->NetID()); +} + +void +NetGameServer::DoGameOver(NetMsg* msg) +{ + if (!msg) return; + Print("Server received Game Over from NetID: %08x\n", msg->NetID()); +} + +void +NetGameServer::DoDisconnect(NetMsg* msg) +{ + if (!msg) return; + Print("Server received Disconnect from NetID: %08x\n", msg->NetID()); +} + +void +NetGameServer::DoObjLoc(NetMsg* msg) +{ + if (!msg) return; + + NetObjLoc obj_loc; + obj_loc.Unpack(msg->Data()); + + NetPlayer* player = FindPlayerByObjID(obj_loc.GetObjID()); + if (player && player->GetShip()) { + player->DoObjLoc(&obj_loc); + } + + else { + player = FindZombieByObjID(obj_loc.GetObjID()); + + if (player) + SendDisconnect(player); + } +} + +void +NetGameServer::DoObjDamage(NetMsg* msg) +{ + if (!msg) return; + Print("Server received OBJ DAMAGE from NetID: %08x (ignored)\n", msg->NetID()); +} + +void +NetGameServer::DoObjKill(NetMsg* msg) +{ + if (!msg) return; + + NetObjKill obj_kill; + obj_kill.Unpack(msg->Data()); + + if (obj_kill.GetKillType() != NetObjKill::KILL_DOCK) { + Print("Server received OBJ KILL from NetID: %08x (ignored)\n", msg->NetID()); + return; + } + + Ship* ship = FindShipByObjID(obj_kill.GetObjID()); + if (ship) { + Ship* killer = FindShipByObjID(obj_kill.GetKillerID()); + Text killer_name = ContentBundle::GetInstance()->GetText("NetGameServer.unknown"); + + if (killer) + killer_name = killer->Name(); + + ShipStats* killee = ShipStats::Find(ship->Name()); + if (killee) + killee->AddEvent(SimEvent::DOCK, killer_name); + + FlightDeck* deck = killer->GetFlightDeck(obj_kill.GetFlightDeck()); + sim->NetDockShip(ship, killer, deck); + } +} + +void +NetGameServer::DoObjSpawn(NetMsg* msg) +{ + if (!msg) return; + Print("Server received OBJ SPAWN from NetID: %08x (ignored)\n", msg->NetID()); +} + +void +NetGameServer::DoObjHyper(NetMsg* msg) +{ + if (!msg) return; + Print("Server received OBJ HYPER from NetID: %d\n", msg->NetID()); + + NetObjHyper obj_hyper; + obj_hyper.Unpack(msg->Data()); + + NetPlayer* player = FindPlayerByObjID(obj_hyper.GetObjID()); + if (player && player->GetShip()) { + if (player->DoObjHyper(&obj_hyper)) { + SendData(&obj_hyper); + } + } + else { + player = FindZombieByObjID(obj_hyper.GetObjID()); + + if (player) + SendDisconnect(player); + } +} + +void +NetGameServer::DoObjTarget(NetMsg* msg) +{ + if (!msg) return; + + NetObjTarget obj_target; + obj_target.Unpack(msg->Data()); + + NetPlayer* player = FindPlayerByObjID(obj_target.GetObjID()); + if (player) { + player->DoObjTarget(&obj_target); + } + else { + player = FindZombieByObjID(obj_target.GetObjID()); + + if (player) + SendDisconnect(player); + } +} + +void +NetGameServer::DoObjEmcon(NetMsg* msg) +{ + if (!msg) return; + + NetObjEmcon obj_emcon; + obj_emcon.Unpack(msg->Data()); + + NetPlayer* player = FindPlayerByObjID(obj_emcon.GetObjID()); + if (player) { + player->DoObjEmcon(&obj_emcon); + } + else { + player = FindZombieByObjID(obj_emcon.GetObjID()); + + if (player) + SendDisconnect(player); + } +} + +// +--------------------------------------------------------------------+ + +void +NetGameServer::DoSysDamage(NetMsg* msg) +{ + if (!msg) return; + + NetSysDamage sys_damage; + sys_damage.Unpack(msg->Data()); + + NetPlayer* player = FindZombieByObjID(sys_damage.GetObjID()); + + if (player) + SendDisconnect(player); +} + +void +NetGameServer::DoSysStatus(NetMsg* msg) +{ + if (!msg) return; + + NetSysStatus sys_status; + sys_status.Unpack(msg->Data()); + + Ship* ship = FindShipByObjID(sys_status.GetObjID()); + NetPlayer* player = FindPlayerByNetID(msg->NetID()); + + if (ship) { + if (!player || ship->GetObjID() != player->GetObjID()) { + /** + Print("NetGameServer::DoSysStatus - received request for ship '%s' from wrong player %s\n", + ship->Name(), player ? player->Name() : "null"); + **/ + + return; + } + + player->DoSysStatus(&sys_status); + + // rebroadcast: + System* sys = ship->GetSystem(sys_status.GetSystem()); + NetUtil::SendSysStatus(ship, sys); + } + + else { + player = FindZombieByObjID(sys_status.GetObjID()); + + if (player) + SendDisconnect(player); + } +} + +// +--------------------------------------------------------------------+ + +void +NetGameServer::DoElemRequest(NetMsg* msg) +{ + if (!msg) return; + + NetElemRequest elem_request; + elem_request.Unpack(msg->Data()); + + Sim* sim = Sim::GetSim(); + Element* elem = sim->FindElement(elem_request.GetName()); + + if (elem) { + int squadron = -1; + int slots[] = { -1,-1,-1,-1 }; + Ship* carrier = elem->GetCarrier(); + + if (carrier && carrier->GetHangar()) { + Hangar* hangar = carrier->GetHangar(); + + for (int i = 0; i < 4; i++) { + hangar->FindSquadronAndSlot(elem->GetShip(i+1), squadron, slots[i]); + } + } + + NetUtil::SendElemCreate(elem, squadron, slots, false, true); + } +} + +// +--------------------------------------------------------------------+ + +void +NetGameServer::DoElemCreate(NetMsg* msg) +{ + if (!msg) return; + + NetElemCreate elem_create; + elem_create.Unpack(msg->Data()); + + Sim* sim = Sim::GetSim(); + Element* elem = sim->CreateElement(elem_create.GetName(), + elem_create.GetIFF(), + elem_create.GetType()); + + int* load = elem_create.GetLoadout(); + int* slots = elem_create.GetSlots(); + int squadron = elem_create.GetSquadron(); + int code = elem_create.GetObjCode(); + Text target = elem_create.GetObjective(); + bool alert = elem_create.GetAlert(); + + elem->SetIntelLevel(elem_create.GetIntel()); + elem->SetLoadout(load); + + if (code > Instruction::RTB || target.length() > 0) { + Instruction* obj = new Instruction(code, target); + elem->AddObjective(obj); + } + + Ship* carrier = sim->FindShip(elem_create.GetCarrier()); + if (carrier) { + elem->SetCarrier(carrier); + + Hangar* hangar = carrier->GetHangar(); + if (hangar) { + Text squadron_name = hangar->SquadronName(squadron); + elem->SetSquadron(squadron_name); + + FlightDeck* deck = 0; + int queue = 1000; + + for (int i = 0; i < carrier->NumFlightDecks(); i++) { + FlightDeck* d = carrier->GetFlightDeck(i); + + if (d && d->IsLaunchDeck()) { + int dq = hangar->PreflightQueue(d); + + if (dq < queue) { + queue = dq; + deck = d; + } + } + } + + for (int i = 0; i < 4; i++) { + int slot = slots[i]; + if (slot > -1) { + hangar->GotoAlert(squadron, slot, deck, elem, load, !alert); + } + } + } + } + + NetUtil::SendElemCreate(elem, + elem_create.GetSquadron(), + elem_create.GetSlots(), + elem_create.GetAlert()); +} + +void +NetGameServer::DoShipLaunch(NetMsg* msg) +{ + if (!msg) return; + + NetShipLaunch ship_launch; + ship_launch.Unpack(msg->Data()); + + Sim* sim = Sim::GetSim(); + int squadron = ship_launch.GetSquadron(); + int slot = ship_launch.GetSlot(); + + NetPlayer* player = FindPlayerByObjID(ship_launch.GetObjID()); + if (player) { + Ship* carrier = player->GetShip(); + + if (carrier) { + Hangar* hangar = carrier->GetHangar(); + + if (hangar) { + hangar->Launch(squadron, slot); + } + + NetUtil::SendShipLaunch(carrier, squadron, slot); + } + } +} + +void +NetGameServer::DoNavData(NetMsg* msg) +{ + if (!msg) return; + + NetNavData nav_data; + nav_data.Unpack(msg->Data()); + + Element* elem = sim->FindElement(nav_data.GetElem()); + + if (elem) { + if (nav_data.IsAdd()) { + Instruction* navpt = new Instruction(*nav_data.GetNavPoint()); + Instruction* after = 0; + int index = nav_data.GetIndex(); + + if (index >= 0 && index < elem->GetFlightPlan().size()) + after = elem->GetFlightPlan().at(index); + + elem->AddNavPoint(navpt, after, false); + } + + else { + Instruction* navpt = nav_data.GetNavPoint(); + Instruction* exist = 0; + int index = nav_data.GetIndex(); + + if (navpt && index >= 0 && index < elem->GetFlightPlan().size()) { + exist = elem->GetFlightPlan().at(index); + *exist = *navpt; + } + } + + SendData(&nav_data); + } +} + +void +NetGameServer::DoNavDelete(NetMsg* msg) +{ + if (!msg) return; + + NetNavDelete nav_delete; + nav_delete.Unpack(msg->Data()); + + Element* elem = sim->FindElement(nav_delete.GetElem()); + + if (elem) { + int index = nav_delete.GetIndex(); + + if (index < 0) { + elem->ClearFlightPlan(false); + } + + else if (index < elem->FlightPlanLength()) { + Instruction* npt = elem->GetFlightPlan().at(index); + elem->DelNavPoint(npt, false); + } + + SendData(&nav_delete); + } +} + +void +NetGameServer::DoWepTrigger(NetMsg* msg) +{ + if (!msg) return; + + NetWepTrigger trigger; + trigger.Unpack(msg->Data()); + + NetPlayer* player = FindPlayerByObjID(trigger.GetObjID()); + if (player) { + player->DoWepTrigger(&trigger); + } + else { + player = FindZombieByObjID(trigger.GetObjID()); + + if (player) + SendDisconnect(player); + } +} + +void +NetGameServer::DoWepRelease(NetMsg* msg) +{ + if (!msg) return; + + NetWepRelease release; + release.Unpack(msg->Data()); + + NetPlayer* player = FindPlayerByObjID(release.GetObjID()); + if (player) { + player->DoWepRelease(&release); + } + else { + player = FindZombieByObjID(release.GetObjID()); + + if (player) + SendDisconnect(player); + } + + Print("WEP RELEASE on server? objid = %d\n", release.GetObjID()); +} + +void +NetGameServer::DoWepDestroy(NetMsg* msg) +{ +} + +void +NetGameServer::DoCommMsg(NetMsg* msg) +{ + if (!msg) return; + + NetCommMsg comm_msg; + comm_msg.Unpack(msg->Data()); + + RadioMessage* radio_msg = comm_msg.GetRadioMessage(); + + NetPlayer* player = FindPlayerByObjID(comm_msg.GetObjID()); + if (player && radio_msg) { + player->DoCommMessage(&comm_msg); + + int channel = comm_msg.GetRadioMessage()->Channel(); + + ListIter dst = players; + while (++dst) { + NetPlayer* remote_player = dst.value(); + if (remote_player->GetNetID() && + (channel == 0 || channel == remote_player->GetIFF())) { + + BYTE* data = comm_msg.Pack(); + int size = comm_msg.Length(); + + link->SendMessage(remote_player->GetNetID(), data, size, NetMsg::RELIABLE); + } + } + } + else { + player = FindZombieByObjID(comm_msg.GetObjID()); + + if (player) + SendDisconnect(player); + } +} + +void +NetGameServer::DoChatMsg(NetMsg* msg) +{ + if (!msg) return; + + NetChatMsg chat_msg; + chat_msg.Unpack(msg->Data()); + + RouteChatMsg(chat_msg); +} + +void +NetGameServer::RouteChatMsg(NetChatMsg& chat_msg) +{ + DWORD dst_id = chat_msg.GetDstID(); + + // broadcast or team: + if (dst_id == 0xffff || dst_id <= 10) { + BYTE* data = chat_msg.Pack(); + int size = chat_msg.Length(); + + ListIter dst = players; + while (++dst) { + NetPlayer* remote_player = dst.value(); + if (remote_player->GetNetID() && chat_msg.GetName() != remote_player->Name()) { + if (dst_id == 0xffff || dst_id == 0 || remote_player->GetIFF() == (int) dst_id-1) + link->SendMessage(remote_player->GetNetID(), data, size); + } + } + + if (local_player && (dst_id == 0xffff || dst_id == 0 || local_player->GetIFF() == (int) dst_id-1)) { + Text name = chat_msg.GetName(); + if (name.length() < 1) + name = ContentBundle::GetInstance()->GetText("NetGameServer.chat.unknown"); + + // don't echo general messages from the local player. + // they are already displayed by the chat entry code + // in starshatter.cpp + + if (name != player_name) + HUDView::Message("%s> %s", name.data(), chat_msg.GetText().data()); + } + } + + // direct to local player: + else if (local_player && local_player->GetObjID() == dst_id) { + Text name = chat_msg.GetName(); + if (name.length() < 1) + name = ContentBundle::GetInstance()->GetText("NetGameServer.chat.unknown"); + + HUDView::Message("%s> %s", name.data(), chat_msg.GetText().data()); + } + + // ship-to-ship, but not to local player: + else if (!local_player || local_player->GetObjID() != dst_id) { + NetPlayer* remote_player = FindPlayerByObjID(dst_id); + if (remote_player && remote_player->GetNetID()) { + BYTE* data = chat_msg.Pack(); + int size = chat_msg.Length(); + link->SendMessage(remote_player->GetNetID(), data, size); + } + + // record message in server log: + ::Print("%s> %s\n", chat_msg.GetName().data(), chat_msg.GetText().data()); + } + + if (dst_id == 0xffff) + return; + + // record message in chat log: + NetLobbyServer* lobby = NetLobbyServer::GetInstance(); + + if (!lobby) + return; + + NetUser* user = lobby->FindUserByName(chat_msg.GetName()); + + if (user) + lobby->AddChat(user, chat_msg.GetText(), false); // don't re-route +} + +// +--------------------------------------------------------------------+ + +const char* FormatGameTime(); + +void +NetGameServer::DoSelfDestruct(NetMsg* msg) +{ + if (!msg) return; + + NetSelfDestruct self_destruct; + self_destruct.Unpack(msg->Data()); + + Ship* ship = FindShipByObjID(self_destruct.GetObjID()); + NetPlayer* player = FindPlayerByNetID(msg->NetID()); + + if (ship) { + if (!player || ship->GetObjID() != player->GetObjID()) { + Print("NetGameServer::DoSelfDestruct - received request for ship '%s' from wrong player %s\n", + ship->Name(), player ? player->Name() : "null"); + + return; + } + + ship->InflictNetDamage(self_destruct.GetDamage()); + + SendData(&self_destruct); + + int ship_destroyed = (!ship->InTransition() && ship->Integrity() < 1.0f); + + // then delete the ship: + if (ship_destroyed) { + NetUtil::SendObjKill(ship, 0, NetObjKill::KILL_MISC); + Print(" %s Self Destruct (%s)\n", ship->Name(), FormatGameTime()); + + ShipStats* killee = ShipStats::Find(ship->Name()); + if (killee) + killee->AddEvent(SimEvent::DESTROYED, ship->Name()); + + ship->DeathSpiral(); + } + } +} + +// +--------------------------------------------------------------------+ + +void +NetGameServer::Send() +{ + if (players.isEmpty()) + return; + + DWORD time = Clock::GetInstance()->GameTime(); + + // don't flood the network... + if (time - last_send_time < MIN_NET_FRAME) + return; + + last_send_time = time; + + // tell the remote players where *we* are: + if (local_player && !local_player->IsNetObserver() && objid) { + double r, p, y; + local_player->Cam().Orientation().ComputeEulerAngles(r,p,y); + + NetObjLoc obj_loc; + obj_loc.SetObjID(objid); + obj_loc.SetLocation(local_player->Location()); + obj_loc.SetVelocity(local_player->Velocity()); + obj_loc.SetOrientation(Point(r,p,y)); + obj_loc.SetThrottle(local_player->Throttle() > 10); + obj_loc.SetAugmenter(local_player->Augmenter()); + obj_loc.SetGearDown(local_player->IsGearDown()); + + Shield* shield = local_player->GetShield(); + if (shield) + obj_loc.SetShield((int) shield->GetPowerLevel()); + else + obj_loc.SetShield(0); + + SendData(&obj_loc); + } + + // tell each remote player where all the others are: + ListIter src = players; + while (++src) { + NetPlayer* player = src.value(); + + Ship* player_ship = player->GetShip(); + + if (player_ship) { + double r, p, y; + player_ship->Cam().Orientation().ComputeEulerAngles(r,p,y); + + NetObjLoc obj_loc; + obj_loc.SetObjID(player->GetObjID()); + obj_loc.SetLocation(player_ship->Location()); + obj_loc.SetVelocity(player_ship->Velocity()); + obj_loc.SetOrientation(Point(r,p,y)); + obj_loc.SetThrottle(player_ship->Throttle() > 10); + obj_loc.SetAugmenter(player_ship->Augmenter()); + obj_loc.SetGearDown(player_ship->IsGearDown()); + + Shield* shield = player_ship->GetShield(); + if (shield) + obj_loc.SetShield((int) shield->GetPowerLevel()); + else + obj_loc.SetShield(0); + + BYTE* obj_loc_data = obj_loc.Pack(); + + ListIter dst = players; + while (++dst) { + NetPlayer* remote_player = dst.value(); + if (remote_player->GetNetID() && remote_player != player) + link->SendMessage(remote_player->GetNetID(), obj_loc_data, NetObjLoc::SIZE); + } + } + } + + // tell each remote player where all the A.I. ships are: + ListIter ai_iter = ships; + while (++ai_iter) { + Ship* s = ai_iter.value(); + + if (s && !s->IsStatic()) { + double r, p, y; + s->Cam().Orientation().ComputeEulerAngles(r,p,y); + + NetObjLoc obj_loc; + obj_loc.SetObjID(s->GetObjID()); + obj_loc.SetLocation(s->Location()); + obj_loc.SetVelocity(s->Velocity()); + obj_loc.SetOrientation(Point(r,p,y)); + obj_loc.SetThrottle(s->Throttle() > 10); + obj_loc.SetAugmenter(s->Augmenter()); + obj_loc.SetGearDown(s->IsGearDown()); + + Shield* shield = s->GetShield(); + if (shield) + obj_loc.SetShield((int) shield->GetPowerLevel()); + else + obj_loc.SetShield(0); + + SendData(&obj_loc); + } + } +} + +// +--------------------------------------------------------------------+ + +void +NetGameServer::SendData(NetData* net_data) +{ + if (net_data) { + BYTE* data = net_data->Pack(); + int size = net_data->Length(); + BYTE flags = 0; + bool all = true; // include player with objid in net_data? + DWORD oid = net_data->GetObjID(); + + BYTE msg_type = net_data->Type(); + + if (msg_type >= 0x10) + flags |= NetMsg::RELIABLE; + + if (msg_type == NET_WEP_TRIGGER || + msg_type == NET_COMM_MESSAGE || + msg_type == NET_CHAT_MESSAGE || + msg_type == NET_OBJ_HYPER || + msg_type == NET_ELEM_CREATE || + msg_type == NET_NAV_DATA || + msg_type == NET_NAV_DELETE) { + all = false; + } + + ListIter dst = players; + while (++dst) { + NetPlayer* remote_player = dst.value(); + if (remote_player->GetNetID() && (all || oid != remote_player->GetObjID())) + link->SendMessage(remote_player->GetNetID(), data, size, flags); + } + } +} + +void +NetGameServer::SendDisconnect(NetPlayer* zombie) +{ + if (zombie) { + NetDisconnect disconnect; + BYTE* data = disconnect.Pack(); + int size = disconnect.Length(); + BYTE flags = NetMsg::RELIABLE; + + if (zombie->GetNetID()) + link->SendMessage(zombie->GetNetID(), data, size, flags); + } +} + +// +--------------------------------------------------------------------+ + +bool +NetGameServer::Update(SimObject* obj) +{ + if (obj->Type() == SimObject::SIM_SHIP) { + Ship* s = (Ship*) obj; + if (local_player == s) + local_player = 0; + + if (ships.contains(s)) + ships.remove(s); + } + + return SimObserver::Update(obj); +} + +const char* +NetGameServer::GetObserverName() const +{ + return "NetGameServer"; +} + +// +--------------------------------------------------------------------+ + +void +NetGameServer::Respawn(DWORD oid, Ship* spawn) +{ + if (!oid || !spawn) return; + + Print("NetGameServer::Respawn(%d, %s)\n", oid, spawn->Name()); + spawn->SetObjID(oid); + Observe(spawn); + + NetPlayer* p = FindPlayerByObjID(oid); + if (p) + p->SetShip(spawn); + else + ships.append(spawn); + + if (objid == oid) { + Print(" RESPAWN LOCAL PLAYER\n\n"); + local_player = spawn; + } +} -- cgit v1.1