summaryrefslogtreecommitdiffhomepage
path: root/StarsEx/NetLobbyClient.cpp
diff options
context:
space:
mode:
authorAki <please@ignore.pl>2022-04-01 21:23:39 +0200
committerAki <please@ignore.pl>2022-04-01 21:23:39 +0200
commit3c487c5cd69c53d6fea948643c0a76df03516605 (patch)
tree72730c7b8b26a5ef8fc9a987ec4c16129efd5aac /StarsEx/NetLobbyClient.cpp
parent8f353abd0bfe18baddd8a8250ab7c4f2d1c83a6e (diff)
downloadstarshatter-3c487c5cd69c53d6fea948643c0a76df03516605.zip
starshatter-3c487c5cd69c53d6fea948643c0a76df03516605.tar.gz
starshatter-3c487c5cd69c53d6fea948643c0a76df03516605.tar.bz2
Moved Stars45 to StarsEx
Diffstat (limited to 'StarsEx/NetLobbyClient.cpp')
-rw-r--r--StarsEx/NetLobbyClient.cpp807
1 files changed, 807 insertions, 0 deletions
diff --git a/StarsEx/NetLobbyClient.cpp b/StarsEx/NetLobbyClient.cpp
new file mode 100644
index 0000000..ca0ff81
--- /dev/null
+++ b/StarsEx/NetLobbyClient.cpp
@@ -0,0 +1,807 @@
+/* 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
+ ========
+ Stream-oriented network client class
+*/
+
+
+#include "NetLobbyClient.h"
+#include "NetClientConfig.h"
+#include "NetAuth.h"
+#include "NetChat.h"
+#include "Campaign.h"
+#include "Player.h"
+#include "Starshatter.h"
+#include "ModInfo.h"
+
+#include "NetHost.h"
+#include "NetPeer.h"
+#include "NetLink.h"
+#include "NetMsg.h"
+#include "NetLayer.h"
+#include "FormatUtil.h"
+#include "VersionInfo.h"
+
+// +-------------------------------------------------------------------+
+
+NetLobbyClient::NetLobbyClient()
+: NetLobby(false), server_id(0), host(false), exit_code(0), temporary(false)
+{
+ NetHost me;
+ Text server_name;
+ WORD port = 11101;
+
+ ping_req_time = 0;
+ chat_req_time = 0;
+ user_req_time = 0;
+ camp_req_time = 0;
+ unit_req_time = 0;
+ mods_req_time = 0;
+
+ NetClientConfig* ncc = NetClientConfig::GetInstance();
+ if (ncc) {
+ NetServerInfo* info = ncc->GetSelectedServer();
+
+ if (info) {
+ server_name = info->hostname;
+ addr = info->addr;
+ port = info->port;
+ gamepass = info->password;
+ }
+ }
+
+ if (server_name.length() && port > 0) {
+ Print(" '%s' is a client of '%s'\n", me.Name(), server_name.data());
+ link = new NetLink;
+ server_id = link->AddPeer(NetAddr(server_name, port));
+ }
+ else if (port == 0) {
+ Print(" '%s' invalid lobby port number %d\n", me.Name(), port);
+ }
+ else {
+ Print(" '%s' is a client without a server\n", me.Name());
+ }
+}
+
+NetLobbyClient::NetLobbyClient(const NetAddr& server_addr)
+: NetLobby(true), server_id(0), addr(server_addr), host(false), exit_code(0),
+temporary(true)
+{
+ ping_req_time = 0;
+ chat_req_time = 0;
+ user_req_time = 0;
+ camp_req_time = 0;
+ unit_req_time = 0;
+ mods_req_time = 0;
+
+ if (addr.IPAddr() != 0) {
+ link = new NetLink;
+ server_id = link->AddPeer(addr);
+ }
+}
+
+NetLobbyClient::~NetLobbyClient()
+{
+ missions.destroy();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetLobbyClient::SendData(int type, Text msg)
+{
+ if (link && server_id && type > 0 && type < 255) {
+ if (msg.length())
+ link->SendMessage(server_id, (BYTE) type, msg.data(), msg.length(), NetMsg::RELIABLE);
+ else
+ link->SendMessage(server_id, (BYTE) type, 0, 0, NetMsg::RELIABLE);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetLobbyClient::ExecFrame()
+{
+ NetLobby::ExecFrame();
+
+ if (!temporary) {
+ // check health of server:
+ NetPeer* s = link->FindPeer(server_id);
+ if (s && (NetLayer::GetUTC() - s->LastReceiveTime() > NET_DISCONNECT_TIME)) {
+ exit_code = 2;
+ }
+
+ // send keep alive ping:
+ if ((NetLayer::GetUTC() - ping_req_time > NET_DISCONNECT_TIME/3)) {
+ SendData(NET_LOBBY_SERVER_INFO, Text());
+ ping_req_time = NetLayer::GetUTC();
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+NetLobbyClient::Login(bool host_req)
+{
+ Player* player = Player::GetCurrentPlayer();
+ if (!player)
+ return false;
+
+ host = host_req;
+
+ Text login = "name \"";
+ login += SafeQuotes(player->Name());
+ login += "\" pass \"";
+ login += SafeQuotes(player->Password());
+ login += "\" version \"";
+ login += versionInfo;
+ login += "\" ";
+
+ char buffer[256];
+
+ sprintf_s(buffer, "host %s rank %d time %d miss %d kill %d loss %d ",
+ host ? "true" : "false",
+ player->Rank(),
+ player->FlightTime(),
+ player->Missions(),
+ player->Kills(),
+ player->Losses());
+
+ login += buffer;
+
+ login += "sig \"";
+ login += SafeQuotes(player->Signature());
+ login += "\" squad \"";
+ login += SafeQuotes(player->Squadron());
+ login += "\" ";
+
+ if (gamepass.length() > 0) {
+ login += "gamepass \"";
+ login += SafeQuotes(gamepass);
+ login += "\" ";
+ }
+
+ SendData(NET_LOBBY_LOGIN, login);
+ ExecFrame();
+ return true;
+}
+
+bool
+NetLobbyClient::Logout()
+{
+ if (host)
+ GameStop();
+
+ SendData(NET_LOBBY_LOGOUT, Text());
+ Sleep(250);
+ ExecFrame();
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+NetLobbyClient::Ping()
+{
+ Text no_data;
+
+ SendData(NET_LOBBY_PING, no_data);
+ Sleep(100);
+
+ SendData(NET_LOBBY_PING, no_data);
+ Sleep(100);
+
+ SendData(NET_LOBBY_PING, no_data);
+ Sleep(100);
+
+ SendData(NET_LOBBY_SERVER_INFO, no_data);
+ Sleep(700);
+ ExecFrame();
+
+ return (server_info.status > NetServerInfo::OFFLINE);
+}
+
+void
+NetLobbyClient::GameStart()
+{
+ SendData(NET_LOBBY_GAME_START, Text());
+ Sleep(100);
+
+ SetStatus(NetServerInfo::ACTIVE);
+ Starshatter::GetInstance()->SetGameMode(Starshatter::PREP_MODE);
+
+ // discard unit map selection data so that
+ // it will be refreshed when we return to
+ // the lobby after the mission:
+
+ ClearUnitMap();
+}
+
+void
+NetLobbyClient::GameStop()
+{
+ SendData(NET_LOBBY_GAME_STOP, Text());
+ ExecFrame();
+ Sleep(100);
+
+ SetStatus(NetServerInfo::LOBBY);
+}
+
+// +--------------------------------------------------------------------+
+
+const Text&
+NetLobbyClient::GetMachineInfo()
+{
+ if (server_info.status > NetServerInfo::OFFLINE)
+ return server_info.machine_info;
+
+ return NetLobby::GetMachineInfo();
+}
+
+int
+NetLobbyClient::GetStatus() const
+{
+ if (server_info.status > NetServerInfo::OFFLINE)
+ return server_info.status;
+
+ return NetLobby::GetStatus();
+}
+
+int
+NetLobbyClient::NumUsers()
+{
+ if (server_info.status > NetServerInfo::OFFLINE)
+ return server_info.nplayers;
+
+ return NetLobby::NumUsers();
+}
+
+bool
+NetLobbyClient::HasHost()
+{
+ if (server_info.status > NetServerInfo::OFFLINE)
+ return server_info.hosted ? true : false;
+
+ return NetLobby::HasHost();
+}
+
+WORD
+NetLobbyClient::GetGamePort()
+{
+ if (server_info.status > NetServerInfo::OFFLINE)
+ return server_info.gameport;
+
+ return NetLobby::GetGamePort();
+}
+
+// +-------------------------------------------------------------------+
+
+void
+NetLobbyClient::AddChat(NetUser* user, const char* msg, bool route)
+{
+ if (!msg || !*msg) return;
+
+ char buffer[280];
+ sprintf_s(buffer, "msg \"%s\"", SafeQuotes(msg));
+
+ SendData(NET_LOBBY_CHAT, buffer);
+ ExecFrame();
+}
+
+List<NetChatEntry>&
+NetLobbyClient::GetChat()
+{
+ if (chat_log.size() < 1 && (NetLayer::GetUTC() - chat_req_time > 3)) {
+ SendData(NET_LOBBY_CHAT, Text());
+ chat_req_time = NetLayer::GetUTC();
+ }
+
+ return chat_log;
+}
+
+List<NetUser>&
+NetLobbyClient::GetUsers()
+{
+ if (users.size() < 1 && (NetLayer::GetUTC() - user_req_time > 2)) {
+ SendData(NET_LOBBY_USER_LIST, Text());
+ user_req_time = NetLayer::GetUTC();
+ }
+
+ return users;
+}
+
+List<ModInfo>&
+NetLobbyClient::GetServerMods()
+{
+ if (server_mods.size() < 1 && (NetLayer::GetUTC() - mods_req_time > 2)) {
+ SendData(NET_LOBBY_SERVER_MODS, Text());
+ mods_req_time = NetLayer::GetUTC();
+ }
+
+ return server_mods;
+}
+
+List<NetUnitEntry>&
+NetLobbyClient::GetUnitMap()
+{
+ bool request = selected_mission &&
+ unit_map.size() < 1 &&
+ (NetLayer::GetUTC() - unit_req_time > 2);
+
+ if (selected_mission && GetStatus() == NetServerInfo::ACTIVE && (NetLayer::GetUTC() - unit_req_time > 5))
+ request = true;
+
+ if (request) {
+ SendData(NET_LOBBY_UNIT_LIST, Text());
+ unit_req_time = NetLayer::GetUTC();
+ }
+
+ return unit_map;
+}
+
+// +--------------------------------------------------------------------+
+
+List<NetCampaignInfo>&
+NetLobbyClient::GetCampaigns()
+{
+ if (campaigns.size() < 1 && (NetLayer::GetUTC() - camp_req_time > 3)) {
+ SendData(NET_LOBBY_MISSION_LIST, Text());
+ camp_req_time = NetLayer::GetUTC();
+ }
+
+ return campaigns;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetLobbyClient::BanUser(NetUser* user)
+{
+ char buffer[512];
+ sprintf_s(buffer, "user \"%s\"", SafeQuotes(user->Name()));
+ SendData(NET_LOBBY_BAN_USER, buffer);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetLobbyClient::SelectMission(DWORD id)
+{
+ char buffer[32];
+ sprintf_s(buffer, "m_id 0x%08x", id);
+ SendData(NET_LOBBY_MISSION_SELECT, buffer);
+}
+
+void
+NetLobbyClient::MapUnit(int n, const char* user, bool lock)
+{
+ if (user && strlen(user) > 250)
+ return;
+
+ char buffer[512];
+ sprintf_s(buffer, "id %d user \"%s\" lock %s",
+ n, SafeQuotes(user), lock ? "true" : "false");
+ SendData(NET_LOBBY_MAP_UNIT, buffer);
+}
+
+Mission*
+NetLobbyClient::GetSelectedMission()
+{
+ mission = 0;
+
+ // ask server for mission:
+ SendData(NET_LOBBY_MISSION_DATA, Text());
+
+ // wait for answer:
+ int i = 150;
+ while (i-- > 0 && !mission) {
+ Sleep(100);
+ ExecFrame();
+ }
+
+ return mission;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetLobbyClient::DoAuthUser(NetPeer* peer, Text msg)
+{
+ List<NetLobbyParam> params;
+ ParseMsg(msg, params);
+
+ int level = NetAuth::NET_AUTH_STANDARD;
+ Text salt;
+
+ for (int i = 0; i < params.size(); i++) {
+ NetLobbyParam* p = params[i];
+
+ int num = 0;
+ sscanf_s(p->value, "%d", &num);
+
+ if (p->name == "level") {
+ level = num;
+ }
+
+ else if (p->name == "salt") {
+ salt = p->value;
+ }
+ }
+
+ Text response = NetAuth::CreateAuthResponse(level, salt);
+ if (response.length() > 0)
+ SendData(NET_LOBBY_USER_AUTH, response);
+}
+
+void
+NetLobbyClient::DoServerInfo(NetPeer* peer, Text msg)
+{
+ List<NetLobbyParam> params;
+ ParseMsg(msg, params);
+
+ for (int i = 0; i < params.size(); i++) {
+ NetLobbyParam* p = params[i];
+
+ int num = 0;
+ sscanf_s(p->value, "%d", &num);
+
+ if (p->name == "info") {
+ server_info.machine_info = p->value;
+ }
+
+ else if (p->name == "version") {
+ server_info.version = p->value;
+ }
+
+ else if (p->name == "mode") {
+ server_info.status = num;
+ }
+
+ else if (p->name == "users") {
+ server_info.nplayers = num;
+ }
+
+ else if (p->name == "host") {
+ server_info.hosted = (p->value == "true");
+ }
+
+ else if (p->name == "port") {
+ server_info.gameport = (WORD) num;
+ }
+ }
+
+ params.destroy();
+}
+
+void
+NetLobbyClient::DoChat(NetPeer* peer, Text msg)
+{
+ List<NetLobbyParam> params;
+ ParseMsg(msg, params);
+
+ int id = 0;
+ Text user_name;
+ Text chat_msg;
+
+ for (int i = 0; i < params.size(); i++) {
+ NetLobbyParam* p = params[i];
+
+ int num = 0;
+ sscanf_s(p->value, "%d", &num);
+
+ if (p->name == "id")
+ id = num;
+
+ else if (p->name == "user")
+ user_name = p->value;
+
+ else if (p->name == "msg")
+ chat_msg = p->value;
+ }
+
+ params.destroy();
+
+ // receive chat from server:
+ if (id && chat_msg.length()) {
+ NetChatEntry* entry = new NetChatEntry(id, user_name, chat_msg);
+
+ if (!chat_log.contains(entry))
+ chat_log.insertSort(entry);
+ else
+ delete entry; // received duplicate
+ }
+}
+
+void
+NetLobbyClient::DoServerMods(NetPeer* peer, Text msg)
+{
+ mods_req_time = NetLayer::GetUTC() + 3600;
+
+ List<NetLobbyParam> params;
+ ParseMsg(msg, params);
+ server_mods.destroy();
+
+ Text name;
+ Text version;
+ Text url;
+
+ for (int i = 0; i < params.size(); i++) {
+ NetLobbyParam* p = params[i];
+
+ if (p->name == "mod") {
+ name = p->value;
+ }
+
+ else if (p->name == "url") {
+ url = p->value;
+ }
+
+ else if (p->name == "ver") {
+ version = p->value;
+
+ ModInfo* info = new ModInfo(name, version, url);
+ server_mods.append(info);
+ }
+ }
+
+ params.destroy();
+}
+
+void
+NetLobbyClient::DoUserList(NetPeer* peer, Text msg)
+{
+ List<NetLobbyParam> params;
+ ParseMsg(msg, params);
+
+ users.destroy();
+
+ Text user_name;
+ Text host_flag;
+ Text signature;
+ Text squadron;
+ int rank = 0;
+ int flight_time = 0;
+ int mission_count = 0;
+ int kills = 0;
+ int losses = 0;
+
+ for (int i = 0; i < params.size(); i++) {
+ NetLobbyParam* p = params[i];
+
+ int num = 0;
+ sscanf_s(p->value, "%d", &num);
+
+ if (p->name == "name")
+ user_name = p->value;
+
+ else if (p->name == "sig")
+ signature = p->value;
+
+ else if (p->name == "squad")
+ squadron = p->value;
+
+ else if (p->name == "rank")
+ rank = num;
+
+ else if (p->name == "time")
+ flight_time = num;
+
+ else if (p->name == "miss")
+ mission_count = num;
+
+ else if (p->name == "kill")
+ kills = num;
+
+ else if (p->name == "loss")
+ losses = num;
+
+ else if (p->name == "host") {
+ host_flag = p->value;
+
+ NetUser* u = new NetUser(user_name);
+ u->SetHost((host_flag == "true") ? true : false);
+ u->SetSignature(signature);
+ u->SetSquadron(squadron);
+ u->SetRank(rank);
+ u->SetFlightTime(flight_time);
+ u->SetMissions(mission_count);
+ u->SetKills(kills);
+ u->SetLosses(losses);
+
+ AddUser(u);
+ }
+ }
+
+ params.destroy();
+}
+
+void
+NetLobbyClient::DoMissionList(NetPeer* peer, Text msg)
+{
+ List<NetLobbyParam> params;
+ ParseMsg(msg, params);
+
+ if (params.size() > 2) {
+ campaigns.destroy();
+
+ NetCampaignInfo* c = 0;
+ MissionInfo* m = 0;
+
+ for (int i = 0; i < params.size(); i++) {
+ NetLobbyParam* p = params[i];
+
+ if (p->name == "c_id") {
+ c = new NetCampaignInfo;
+ sscanf_s(p->value, "0x%x", &c->id);
+ campaigns.append(c);
+
+ m = 0;
+ }
+
+ else if (c && p->name == "c_name") {
+ c->name = p->value;
+ }
+
+ else if (p->name == "m_id") {
+ int id = 0;
+ sscanf_s(p->value, "0x%x", &id);
+
+ int m_id = id & NET_MISSION_MASK;
+ int c_id = id >> NET_CAMPAIGN_SHIFT;
+
+ for (int i = 0; i < campaigns.size(); i++) {
+ NetCampaignInfo* c = campaigns[i];
+ if (c->id == c_id) {
+ m = new MissionInfo;
+ m->id = m_id;
+ c->missions.append(m);
+ missions.append(m); // for later garbage collection
+ break;
+ }
+ }
+ }
+
+ else if (m && p->name == "m_name") {
+ m->name = p->value;
+ }
+
+ else if (m && p->name == "m_desc") {
+ m->description = p->value;
+ }
+ }
+ }
+
+ params.destroy();
+}
+
+void
+NetLobbyClient::DoMissionSelect(NetPeer* peer, Text msg)
+{
+ List<NetLobbyParam> params;
+ ParseMsg(msg, params);
+
+ for (int i = 0; i < params.size(); i++) {
+ NetLobbyParam* p = params[i];
+
+ int num = 0;
+ sscanf_s(p->value, "0x%x", &num);
+
+ if (p->name == "m_id") {
+ if (selected_mission != (DWORD) num) {
+ selected_mission = num;
+ ClearUnitMap();
+ }
+ }
+ }
+
+ params.destroy();
+}
+
+void
+NetLobbyClient::DoMissionData(NetPeer* peer, Text msg)
+{
+ Campaign* c = Campaign::SelectCampaign("Multiplayer Missions");
+
+ if (c) {
+ c->LoadNetMission(99999, msg.data());
+ mission = c->GetMission(99999);
+ }
+
+ if (msg.length()) {
+ FILE* f;
+ ::fopen_s(&f, "multi_mission_recv.def", "wb");
+ if (f) {
+ ::fwrite(msg.data(), msg.length(), 1, f);
+ ::fclose(f);
+ }
+ }
+}
+
+void
+NetLobbyClient::DoUnitList(NetPeer* peer, Text msg)
+{
+ List<NetLobbyParam> params;
+ ParseMsg(msg, params);
+
+ if (params.size() > 2) {
+ ClearUnitMap();
+
+ Text elem_name;
+ Text design;
+ Text user_name;
+ int iff;
+ int index;
+ int lives = 1;
+ int hull = 100;
+ int role = 0;
+ int lock = 0;
+
+ for (int i = 0; i < params.size(); i++) {
+ NetLobbyParam* p = params[i];
+
+ if (p->name == "name") {
+ elem_name = p->value;
+ }
+ else if (p->name == "design") {
+ design = p->value;
+ }
+ else if (p->name == "user") {
+ user_name = p->value;
+ }
+ else if (p->name == "index") {
+ sscanf_s(p->value, "%d", &index);
+ }
+ else if (p->name == "iff") {
+ sscanf_s(p->value, "%d", &iff);
+ }
+ else if (p->name == "lives") {
+ sscanf_s(p->value, "%d", &lives);
+ }
+ else if (p->name == "hull") {
+ sscanf_s(p->value, "%d", &hull);
+ }
+ else if (p->name == "role") {
+ sscanf_s(p->value, "%d", &role);
+ }
+ else if (p->name == "lock") {
+ sscanf_s(p->value, "%d", &lock);
+
+ NetUnitEntry* entry = new NetUnitEntry(elem_name, design, iff, index);
+ entry->SetUserName(user_name);
+ entry->SetLives(lives);
+ entry->SetIntegrity(hull);
+ entry->SetMissionRole(role);
+ entry->SetLock(lock ? true : false);
+
+ unit_map.append(entry);
+ }
+ }
+ }
+
+ params.destroy();
+}
+
+void
+NetLobbyClient::DoMapUnit(NetPeer* peer, Text msg)
+{
+}
+
+void
+NetLobbyClient::DoGameStart(NetPeer* peer, Text msg)
+{
+}
+
+void
+NetLobbyClient::DoExit(NetPeer* peer, Text msg)
+{
+ exit_code = 1;
+}