/* Starshatter OpenSource Distribution Copyright (c) 1997-2004, Destroyer Studios LLC. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name "Destroyer Studios" nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. SUBSYSTEM: Stars.exe FILE: NetLobby.cpp AUTHOR: John DiCamillo OVERVIEW ======== Base Class for Multiplayer Game Lobby classes */ #include "MemDebug.h" #include "NetLobby.h" #include "NetLobbyClient.h" #include "NetLobbyServer.h" #include "NetClientConfig.h" #include "NetServerConfig.h" #include "NetChat.h" #include "NetUser.h" #include "NetMsg.h" #include "NetData.h" #include "NetLayer.h" #include "Player.h" #include "Ship.h" #include "ShipDesign.h" #include "Campaign.h" #include "Element.h" #include "Mission.h" #include "NetHost.h" #include "Game.h" #include "Light.h" #include "parseutil.h" // +--------------------------------------------------------------------+ const int MAX_NET_FPS = 50; const int MIN_NET_FRAME = 1000 / MAX_NET_FPS; const DWORD SHIP_ID_START = 0x0010; const DWORD SHOT_ID_START = 0x0400; static NetLobby* instance = 0; static DWORD ship_id_key = SHIP_ID_START; static DWORD shot_id_key = SHOT_ID_START; // +--------------------------------------------------------------------+ NetLobby::NetLobby(bool temporary) : link(0), local_user(0), last_send_time(0), active(true), selected_mission(0), mission(0), status(0) { if (!temporary) instance = this; } NetLobby::~NetLobby() { if (instance == this) instance = 0; unit_map.destroy(); users.destroy(); campaigns.destroy(); chat_log.destroy(); if (local_user) { delete local_user; local_user = 0; } if (link) { delete link; link = 0; } mission = 0; } // +--------------------------------------------------------------------+ NetLobby* NetLobby::GetInstance() { return instance; } // +--------------------------------------------------------------------+ void NetLobby::ExecFrame() { Send(); Recv(); } void NetLobby::Recv() { NetMsg* msg = link->GetMessage(); while (msg) { if (active) { NetPeer* peer = link->FindPeer(msg->NetID()); static char buffer[256]; char* text = 0; if (msg->Length() > 2) { if (msg->Length() < 250) { ZeroMemory(buffer, sizeof(buffer)); CopyMemory(buffer, msg->Data()+2, msg->Length()-2); text = buffer; } else if (msg->Length() < 1024 * 1024) { text = new(__FILE__,__LINE__) char[msg->Length()]; ZeroMemory(text, msg->Length()); CopyMemory(text, msg->Data()+2, msg->Length()-2); } } switch (msg->Type()) { case NET_LOBBY_PING: DoPing(peer, text); break; case NET_LOBBY_SERVER_INFO: DoServerInfo(peer, text); break; case NET_LOBBY_SERVER_MODS: DoServerMods(peer, text); break; case NET_LOBBY_LOGIN: DoLogin(peer, text); break; case NET_LOBBY_LOGOUT: DoLogout(peer, text); break; case NET_LOBBY_AUTH_USER: DoAuthUser(peer, text); break; case NET_LOBBY_USER_AUTH: DoUserAuth(peer, text); break; case NET_LOBBY_CHAT: DoChat(peer, text); break; case NET_LOBBY_USER_LIST: DoUserList(peer, text); break; case NET_LOBBY_BAN_USER: DoBanUser(peer, text); break; case NET_LOBBY_MISSION_LIST: DoMissionList(peer, text); break; case NET_LOBBY_MISSION_SELECT: DoMissionSelect(peer, text); break; case NET_LOBBY_MISSION_DATA: DoMissionData(peer, text); break; case NET_LOBBY_UNIT_LIST: DoUnitList(peer, text); break; case NET_LOBBY_MAP_UNIT: DoMapUnit(peer, text); break; case NET_LOBBY_GAME_START: DoGameStart(peer, text); break; case NET_LOBBY_GAME_STOP: DoGameStop(peer, text); break; case NET_LOBBY_EXIT: DoExit(peer, text); break; } if (text && text != buffer) delete [] text; } delete msg; msg = link->GetMessage(); } } // +--------------------------------------------------------------------+ void NetLobby::Send() { } // +-------------------------------------------------------------------+ DWORD NetLobby::GetLag() { if (link) return link->GetLag(); return 0; } // +-------------------------------------------------------------------+ void NetLobby::SetLocalUser(NetUser* user) { if (user != local_user) { if (local_user) { delete local_user; local_user = 0; } if (user) { local_user = user; local_user->SetHost(true); } } } void NetLobby::BanUser(NetUser* user) { } void NetLobby::AddUser(NetUser* user) { if (user) { if (user != local_user && !users.contains(user)) { users.append(user); Print("NetLobby User Logged In - name: '%s' id: %d host: %d\n", user->Name().data(), user->GetNetID(), user->IsHost()); } } } void NetLobby::DelUser(NetUser* user) { if (user) { if (user == local_user) local_user = 0; else users.remove(user); UnmapUnit(user->Name()); Print("NetLobby User Logged Out - name: '%s' id: %d host: %d\n", user->Name().data(), user->GetNetID(), user->IsHost()); user->SetHost(false); delete user; } } int NetLobby::NumUsers() { int num = 0; if (local_user) num = 1; num += users.size(); return num; } NetUser* NetLobby::GetHost() { for (int i = 0; i < users.size(); i++) { NetUser* u = users[i]; if (u->IsHost()) return u; } return 0; } bool NetLobby::HasHost() { bool host = false; if (local_user && local_user->IsHost()) host = true; for (int i = 0; i < users.size() && !host; i++) { if (users[i]->IsHost()) host = true; } if (status > NetServerInfo::LOBBY) host = true; return host; } bool NetLobby::SetUserHost(NetUser* user, bool host) { bool ok = false; if (user && users.contains(user)) { if (host && !HasHost()) { user->SetHost(true); ok = true; } else if (!host) { user->SetHost(false); ok = true; } } return ok; } NetUser* NetLobby::GetLocalUser() { return local_user; } List& NetLobby::GetUsers() { return users; } List& NetLobby::GetServerMods() { return server_mods; } NetUser* NetLobby::FindUserByAddr(const NetAddr& addr) { for (int i = 0; i < users.size(); i++) { NetUser* u = users[i]; if (u->GetAddress().IPAddr() == addr.IPAddr()) return u; } return 0; } NetUser* NetLobby::FindUserByName(const char* name) { if (local_user && !_stricmp(local_user->Name(), name)) return local_user; for (int i = 0; i < users.size(); i++) { NetUser* u = users[i]; if (!_stricmp(u->Name(), name)) return u; } return 0; } NetUser* NetLobby::FindUserByNetID(DWORD id) { for (int i = 0; i < users.size(); i++) { NetUser* u = users[i]; if (u->GetNetID() == id) return u; } return 0; } // +--------------------------------------------------------------------+ void NetLobby::ParseMsg(Text msg, List& params) { params.destroy(); BlockReader reader(msg.data(), msg.length()); Scanner lexer(&reader); Token name = lexer.Get(); while (name.type() == Token::AlphaIdent) { Token value = lexer.Get(); if (value.type() != Token::EOT) { Text val = value.symbol(); if (val[0] == '"' && val[val.length()-1] == '"') { val = val.substring(1, val.length()-2); } NetLobbyParam* param = new(__FILE__,__LINE__) NetLobbyParam(name.symbol(), val); params.append(param); name = lexer.Get(); } else { name = Token(Token::EOT); } } } // +--------------------------------------------------------------------+ bool NetLobby::Ping() { Sleep(500); return false; } // +-------------------------------------------------------------------+ void NetLobby::AddChat(NetUser* user, const char* msg, bool route) { if (user && msg && *msg) chat_log.append(new(__FILE__,__LINE__) NetChatEntry(user, msg)); } List& NetLobby::GetChat() { return chat_log; } void NetLobby::ClearChat() { chat_log.destroy(); } // +-------------------------------------------------------------------+ List& NetLobby::GetCampaigns() { return campaigns; } // +-------------------------------------------------------------------+ void NetLobby::AddUnitMap(MissionElement* elem, int index) { if (elem) unit_map.append(new(__FILE__,__LINE__) NetUnitEntry(elem, index)); } List& NetLobby::GetUnitMap() { return unit_map; } void NetLobby::ClearUnitMap() { unit_map.destroy(); } void NetLobby::MapUnit(int n, const char* user, bool lock) { if (n >= 0 && n < unit_map.size()) { NetUnitEntry* unit = unit_map[n]; if (!lock && unit->GetLocked()) return; if (user && *user) { for (int i = 0; i < unit_map.size(); i++) { NetUnitEntry* u = unit_map[i]; if (u->GetUserName() == user) { if (!lock && u->GetLocked()) return; u->SetUserName(""); } } } unit->SetUserName(user); unit->SetLock(lock); } } void NetLobby::UnmapUnit(const char* user) { if (user && *user) { for (int i = 0; i < unit_map.size(); i++) { NetUnitEntry* u = unit_map[i]; if (u->GetUserName() == user) { u->SetUserName(""); u->SetLock(false); return; } } } } bool NetLobby::IsMapped(const char* user) { if (user && *user) { for (int i = 0; i < unit_map.size(); i++) { NetUnitEntry* u = unit_map[i]; if (u->GetUserName() == user) { return true; } } } return false; } // +--------------------------------------------------------------------+ void NetLobby::SelectMission(DWORD id) { if (selected_mission != id) { selected_mission = id; ClearUnitMap(); int mission_id = selected_mission; int campaign_id = -1; Campaign* campaign = 0; mission = 0; campaign_id = mission_id >> NET_CAMPAIGN_SHIFT; mission_id = mission_id & NET_MISSION_MASK; ListIter c_iter = Campaign::GetAllCampaigns(); while (++c_iter) { campaign = c_iter.value(); if (campaign->GetCampaignId() == campaign_id) { mission = campaign->GetMission(mission_id); break; } } if (campaign && mission) { Campaign::SelectCampaign(campaign->Name()); campaign->SetMissionId(mission_id); ListIter iter = mission->GetElements(); while (++iter) { MissionElement* elem = iter.value(); if (elem->IsPlayable()) { if (elem->Count() == 1) { AddUnitMap(elem); } else { for (int i = 0; i < elem->Count(); i++) AddUnitMap(elem, i+1); } } } } else { selected_mission = 0; mission = 0; } } } // +--------------------------------------------------------------------+ bool NetLobby::IsNetLobbyClient() { if (instance) return instance->IsClient(); return false; } bool NetLobby::IsNetLobbyServer() { if (instance) return instance->IsServer(); return false; } // +-------------------------------------------------------------------+ // +-------------------------------------------------------------------+ // +-------------------------------------------------------------------+ NetUnitEntry::NetUnitEntry(MissionElement* e, int n) : index(n), lock(false), lives(1), hull(100), role(0) { if (e) { elem = e->Name(); iff = e->GetIFF(); if (e->GetDesign()) design = e->GetDesign()->name; } } NetUnitEntry::NetUnitEntry(const char* e, const char* d, int i, int n) : elem(e), design(d), iff(i), index(n), lock(false), lives(1), hull(100), role(0) { } NetUnitEntry::~NetUnitEntry() { } Text NetUnitEntry::GetDescription() const { if (elem) { static char buffer[1024]; sprintf_s(buffer, "name \"%s\" index %d design \"%s\" iff %d user \"%s\" lives %d hull %d role %d lock %d ", elem.data(), index, design.data(), iff, user.data(), lives, hull, role, lock); return buffer; } return "name \"Not Found\" "; } // +--------------------------------------------------------------------+ // +--------------------------------------------------------------------+ // +--------------------------------------------------------------------+ NetServerInfo::NetServerInfo() : nplayers(0), hosted(0), status(0), port(0), gameport(0), save(false), ping_time(0) { }