Symphony Of Empires
client_network.cpp
Go to the documentation of this file.
1 // Symphony of Empires
2 // Copyright (C) 2021, Symphony of Empires contributors
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see <https://www.gnu.org/licenses/>.
16 //
17 // ----------------------------------------------------------------------------
18 // Name:
19 // client/client_network.cpp
20 //
21 // Abstract:
22 // Does some important stuff.
23 // ----------------------------------------------------------------------------
24 
25 #include <cstring>
26 #include <cstdio>
27 #include <cstdlib>
28 #include <mutex>
29 #include <chrono>
30 #include <thread>
31 
32 #include "eng3d/log.hpp"
33 
34 #include "action.hpp"
35 #include "unit.hpp"
36 #include "diplomacy.hpp"
37 #include "world.hpp"
39 
40 #include "client/game_state.hpp"
41 #include "client/map.hpp"
42 
43 Client* g_client = nullptr;
44 Client::Client(GameState& _gs, std::string host, const unsigned port)
45  : Eng3D::Networking::Client(host, port),
46  gs{_gs}
47 {
48  g_client = this;
49 
50  // Launch the receive and send thread
51  this->run = true;
52  net_thread = std::thread(&Client::netloop, this);
53  has_snapshot = false;
54 }
55 
56 // The server assumes all clients are able to handle all events regardless of anything
57 // if the client runs out of memory it needs to disconnect and then reconnect in order
58 // to establish a new connection; since the server won't hand out snapshots - wait...
59 // if you need snapshots for any reason (like desyncs) you can request with ActionType::SNAPSHOT
61  {
63  Eng3D::Deser::serialize<ActionType>(ar, ActionType::CONNECT);
65  Eng3D::Networking::Packet packet(fd, ar.get_buffer(), ar.size());
66  packet.send();
67  }
68 
69  has_snapshot = true;
70  this->do_netloop([this]() -> bool {
71  return this->run == true;
72  }, [this](const Eng3D::Networking::Packet &packet, Eng3D::Deser::Archive &ar) -> void {
73  ActionType action;
74  Eng3D::Deser::deserialize(ar, action);
75  if(!gs.host_mode) {
76  const std::scoped_lock lock(gs.world->world_mutex);
77  // Ping from server, we should answer with a pong!
78  switch(action) {
79  // Update/Remove/Add Actions
80  // These actions all follow the same format they give a specialized ID for the index
81  // where the operated object is or should be; this allows for extreme-level fuckery
82  // like ref-name changes in the middle of a game in the case of updates.
83  //
84  // After the ID the object in question is given in a serialized form, in which the
85  // deserializer will deserialize onto the final object; after this the operation
86  // desired is done.
88  NationId size;
89  Eng3D::Deser::deserialize(ar, size);
90  for(size_t i = 0; i < static_cast<size_t>(size); i++) {
91  NationId nation_id;
92  Eng3D::Deser::deserialize(ar, nation_id);
93  auto& nation = gs.world->nations.at(nation_id);
94  Eng3D::Deser::deserialize(ar, nation);
95  }
96  } break;
98  NationId nation_id;
99  Eng3D::Deser::deserialize(ar, nation_id);
100  Policies policy;
101  Eng3D::Deser::deserialize(ar, policy);
102  auto& nation = gs.world->nations.at(nation_id);
103  nation.set_policy(policy);
104  Eng3D::Deser::deserialize(ar, nation.commodity_production);
105  } break;
107  ProvinceId size;
108  Eng3D::Deser::deserialize(ar, size);
109  for(size_t i = 0; i < static_cast<size_t>(size); i++) {
110  ProvinceId province_id;
111  Eng3D::Deser::deserialize(ar, province_id);
112  auto& province = gs.world->provinces.at(province_id);
113 
114  auto old_owner_id = province.owner_id;
115  auto old_controller_id = province.controller_id;
116  Eng3D::Deser::deserialize(ar, province);
117  if(province.owner_id != old_owner_id)
119  if(province.controller_id != old_controller_id)
121  }
122  } break;
124  UnitId size;
125  Eng3D::Deser::deserialize(ar, size);
126  for(size_t i = 0; i < static_cast<size_t>(size); i++) {
127  Unit unit;
128  Eng3D::Deser::deserialize(ar, unit);
129  gs.world->unit_manager.units[unit] = unit;
130  }
131  } break;
132  case ActionType::UNIT_ADD: {
133  Unit unit;
134  Eng3D::Deser::deserialize(ar, unit);
135  ProvinceId province_id;
136  Eng3D::Deser::deserialize(ar, province_id);
137  gs.world->unit_manager.add_unit(unit, province_id);
138  Eng3D::Log::debug("client", translate_format("Adding new unit from nation %s", gs.world->nations[unit.owner_id].ref_name.c_str()));
139  } break;
141  UnitId unit_id;
142  Eng3D::Deser::deserialize(ar, unit_id);
143  gs.world->unit_manager.remove_unit(unit_id);
144  } break;
145  case ActionType::UNIT_MOVE: {
146  UnitId unit_id;
147  Eng3D::Deser::deserialize(ar, unit_id);
148  ProvinceId province_id;
149  Eng3D::Deser::deserialize(ar, province_id);
150  gs.world->unit_manager.move_unit(unit_id, province_id);
151  } break;
153  ProvinceId province_id;
154  Eng3D::Deser::deserialize(ar, province_id);
155  auto& province = gs.world->provinces.at(province_id);
156  BuildingTypeId building_type_id;
157  Eng3D::Deser::deserialize(ar, building_type_id);
158  province.buildings.at(building_type_id).level += 1.f;
159  } break;
161  ProvinceId province_id;
162  Eng3D::Deser::deserialize(ar, province_id);
163  auto& province = gs.world->provinces.at(province_id);
164  BuildingTypeId building_type_id;
165  Eng3D::Deser::deserialize(ar, building_type_id);
166  province.buildings.at(building_type_id).level -= 1.f;
167  } break;
168  case ActionType::TREATY_ADD: {
169  Treaty treaty;
170  Eng3D::Deser::deserialize(ar, treaty);
171  gs.world->insert(treaty);
172  Eng3D::Log::debug("client", translate_format("Adding new treaty sent by %s", gs.world->nations[treaty.sender_id].ref_name.c_str()));
173  for(const auto& [nation_id, _] : treaty.approval_status)
174  Eng3D::Log::debug("client", gs.world->nations[nation_id].ref_name.c_str());
175  } break;
176  case ActionType::WORLD_TICK: {
177  // Give up the world mutex for now
178  gs.update_tick = true;
179  gs.world->time++;
180 
181  decltype(gs.world->time) new_time;
182  Eng3D::Deser::deserialize(ar, new_time);
183  if(gs.world->time != new_time)
184  gs.ui_ctx.prompt("Network", "Desynchronized from server, oops!");
185  } break;
187  ProvinceId province_id;
188  Eng3D::Deser::deserialize(ar, province_id);
189  auto& province = gs.world->provinces.at(province_id);
190  Eng3D::Deser::deserialize(ar, province);
191  } break;
193  NationId nation_id;
194  Eng3D::Deser::deserialize(ar, nation_id);
195  auto& nation = gs.world->nations.at(nation_id);
196  Eng3D::Deser::deserialize(ar, nation.client_username);
197  } break;
198  default:
199  break;
200  }
201  }
202  });
203 }
204 
207  while(!has_snapshot) {
208  // Just wait...
209  }
210 }
211 
213  this->run = false;
214  if(this->net_thread.joinable())
215  this->net_thread.join();
216 }
ActionType
Definition: action.hpp:32
@ SET_USERNAME
Definition: action.hpp:36
@ CONNECT
Definition: action.hpp:37
@ WORLD_TICK
Definition: action.hpp:34
@ PROVINCE_COLONIZE
Definition: action.hpp:42
@ UNIT_MOVE
Definition: action.hpp:50
@ TREATY_ADD
Definition: action.hpp:57
@ BUILDING_ADD
Definition: action.hpp:52
@ NATION_UPDATE
Definition: action.hpp:43
@ BUILDING_REMOVE
Definition: action.hpp:53
@ UNIT_REMOVE
Definition: action.hpp:48
@ UNIT_UPDATE
Definition: action.hpp:46
@ UNIT_ADD
Definition: action.hpp:47
@ PROVINCE_UPDATE
Definition: action.hpp:41
@ NATION_ENACT_POLICY
Definition: action.hpp:44
void wait_for_snapshot()
Waits to receive the server initial world snapshot.
void netloop()
Client(GameState &gs, std::string host, const unsigned port)
void do_netloop(std::function< bool()> cond, std::function< void(const Packet &packet, Eng3D::Deser::Archive &ar)> handler)
Definition: network.cpp:424
UI::Context ui_ctx
Definition: state.hpp:128
bool host_mode
Definition: game_state.hpp:185
std::atomic< bool > update_tick
Definition: game_state.hpp:150
World * world
Definition: game_state.hpp:156
void mark_province_control_changed(ProvinceId province_id)
Definition: province.hpp:183
void mark_province_owner_changed(ProvinceId province_id)
Definition: province.hpp:179
void prompt(const std::string &title, const std::string &text)
Definition: ui.cpp:183
Roughly a batallion, consisting of approximately 500 soldiers each.
Definition: unit.hpp:80
NationId owner_id
Definition: unit.hpp:128
void add_unit(Unit unit, ProvinceId unit_current_province)
Definition: unit.cpp:121
Eng3D::Freelist< Unit > units
Definition: unit.hpp:173
void move_unit(UnitId unit, ProvinceId target_province)
Definition: unit.cpp:151
void remove_unit(UnitId unit)
Definition: unit.cpp:138
UnitManager unit_manager
Definition: world.hpp:145
void insert(T &ptr)
Definition: world.hpp:150
std::mutex world_mutex
Definition: world.hpp:225
ProvinceManager province_manager
Definition: world.hpp:146
int time
Definition: world.hpp:221
Client * g_client
void deserialize(Eng3D::Deser::Archive &ar, T &obj)
Definition: serializer.hpp:154
void serialize(Eng3D::Deser::Archive &ar, const T &obj)
Definition: serializer.hpp:144
void debug(const std::string_view category, const std::string_view msg)
Definition: log.cpp:58
std::string translate_format(const std::string_view format, Args &&... args)
String formatter, with translation.
Definition: string.hpp:128
Base class that serves as archiver, stores (in memory) the data required for serialization/deserializ...
Definition: serializer.hpp:64
std::vector< std::pair< NationId, TreatyApproval > > approval_status
Definition: diplomacy.hpp:277
NationId sender_id
Definition: diplomacy.hpp:272