fix a lot of issues on psogc; add proxy module

- $ann implemented
- concurrency removed; server is now single-threaded, event-driven and much more stable
- rare seed is no longer the game id; ids are sequential from server startup so they weren't random at all before
- supports dropping privileges; now you can run it as root so it can open a sockets on low ports, then it will switch to the given user before serving any traffic
- newserv now behaves like a proxy if you run it with the --proxy-destination=<IP_OR_HOSTNAME> argument; there's also an (invisible) shell in this mode where you can inject commands to the server or client. e.g. it can always be christmas in the lobby if you do `sc DA 01 00 00`
- increased the mtu on PSODolphinConfig's tap0 configuration; this seems to make the connection more stable
- fixed some uninitialized memory bugs
- the shell is now event-driven and now uses libevent too; unfortunately this means readline doesn't work anymore (no history and vim-like shortcuts)
- made network command display consistent for input vs. output (the header appears in both cases now)
- fixed bugs in some subcommand handling (the BB logic was being applied to non-BB clients erroneously, causing most item drops not to work at all)
- fixed player tags in the short lobby data struct. unclear if this was actually a problem but it was inconsistent with other servers
- fixed "unused" field in game join command (actually it appears to be disable_udp and should be 1, not 0)
- cleaned up Server abstraction a bit
- rewrote some text functions; asan was complaining about the built-in ones for some reason
- added an optional welcome message
This commit is contained in:
Martin Michelsen
2020-02-16 15:03:47 -08:00
parent 76c810c1e6
commit 0d4b0b2279
39 changed files with 1487 additions and 975 deletions
+16 -44
View File
@@ -2,6 +2,8 @@
#include <string.h>
#include <phosg/Random.hh>
#include "SendCommands.hh"
#include "Text.hh"
@@ -11,8 +13,9 @@ using namespace std;
Lobby::Lobby() : lobby_id(0), min_level(0), max_level(0xFFFFFFFF),
next_game_item_id(0), version(GameVersion::GC), section_id(0), episode(1),
difficulty(0), mode(0), event(0), block(0), type(0), leader_id(0),
max_clients(12), flags(0), loading_quest_id(0) {
difficulty(0), mode(0), rare_seed(random_object<uint32_t>()), event(0),
block(0), type(0), leader_id(0), max_clients(12), flags(0),
loading_quest_id(0) {
for (size_t x = 0; x < 12; x++) {
this->next_item_id[x] = 0;
@@ -27,7 +30,7 @@ bool Lobby::is_game() const {
return this->flags & LobbyFlag::IsGame;
}
void Lobby::reassign_leader_on_client_departure_locked(size_t leaving_client_index) {
void Lobby::reassign_leader_on_client_departure(size_t leaving_client_index) {
for (size_t x = 0; x < this->max_clients; x++) {
if (x == leaving_client_index) {
continue;
@@ -41,7 +44,6 @@ void Lobby::reassign_leader_on_client_departure_locked(size_t leaving_client_ind
}
bool Lobby::any_client_loading() const {
rw_guard g(this->lock, false);
for (size_t x = 0; x < this->max_clients; x++) {
if (!this->clients[x].get()) {
continue;
@@ -53,7 +55,7 @@ bool Lobby::any_client_loading() const {
return false;
}
size_t Lobby::count_clients_locked() const {
size_t Lobby::count_clients() const {
size_t ret = 0;
for (size_t x = 0; x < this->max_clients; x++) {
if (this->clients[x].get()) {
@@ -63,49 +65,34 @@ size_t Lobby::count_clients_locked() const {
return ret;
}
size_t Lobby::count_clients() const {
rw_guard g(this->lock, false);
return this->count_clients_locked();
}
void Lobby::add_client(shared_ptr<Client> c) {
rw_guard g(this->lock, true);
this->add_client_locked(c);
}
void Lobby::add_client_locked(shared_ptr<Client> c) {
ssize_t index;
for (index = this->max_clients - 1; index >= 0; index--) {
for (index = 0; index < max_clients; index++) {
if (!this->clients[index].get()) {
this->clients[index] = c;
break;
}
}
if (index < 0) {
if (index >= max_clients) {
throw out_of_range("no space left in lobby");
}
c->lobby_client_id = index;
c->lobby_id = this->lobby_id;
// if there's no one else in the lobby, set the leader id as well
if (index == this->max_clients - 1) {
for (index = this->max_clients - 2; index >= 0; index--) {
if (index == 0) {
for (index = 1; index < max_clients; index++) {
if (this->clients[index].get()) {
break;
}
}
if (index < 0) {
if (index >= max_clients) {
this->leader_id = c->lobby_client_id;
}
}
}
void Lobby::remove_client(shared_ptr<Client> c) {
rw_guard g(this->lock, true);
this->remove_client_locked(c);
}
void Lobby::remove_client_locked(shared_ptr<Client> c) {
if (this->clients[c->lobby_client_id] != c) {
auto other_c = this->clients[c->lobby_client_id].get();
throw logic_error(string_printf(
@@ -122,7 +109,7 @@ void Lobby::remove_client_locked(shared_ptr<Client> c) {
c->lobby_id = 0;
}
this->reassign_leader_on_client_departure_locked(c->lobby_client_id);
this->reassign_leader_on_client_departure(c->lobby_client_id);
}
void Lobby::move_client_to_lobby(shared_ptr<Lobby> dest_lobby,
@@ -131,31 +118,18 @@ void Lobby::move_client_to_lobby(shared_ptr<Lobby> dest_lobby,
return;
}
// deadlock prevention: lock the lobbies in increasing order of memory address
vector<rw_guard> guards;
uint8_t* this_ptr = reinterpret_cast<uint8_t*>(this);
uint8_t* dest_ptr = reinterpret_cast<uint8_t*>(dest_lobby.get());
if (this_ptr < dest_ptr) {
guards.emplace_back(this->lock, true);
guards.emplace_back(dest_lobby->lock, true);
} else {
guards.emplace_back(dest_lobby->lock, true);
guards.emplace_back(this->lock, true);
}
if (dest_lobby->count_clients_locked() >= dest_lobby->max_clients) {
if (dest_lobby->count_clients() >= dest_lobby->max_clients) {
throw out_of_range("no space left in lobby");
}
this->remove_client_locked(c);
dest_lobby->add_client_locked(c);
this->remove_client(c);
dest_lobby->add_client(c);
}
shared_ptr<Client> Lobby::find_client(const char16_t* identifier,
uint64_t serial_number) {
rw_guard g(this->lock, false);
for (size_t x = 0; x < this->max_clients; x++) {
if (!this->clients[x]) {
continue;
@@ -190,12 +164,10 @@ uint8_t Lobby::game_event_for_lobby_event(uint8_t lobby_event) {
void Lobby::add_item(const PlayerInventoryItem& item) {
rw_guard g(this->lock, true);
this->item_id_to_floor_item.emplace(item.data.item_id, item);
}
void Lobby::remove_item(uint32_t item_id, PlayerInventoryItem* item) {
rw_guard g(this->lock, true);
auto item_it = this->item_id_to_floor_item.find(item_id);
if (item_it == this->item_id_to_floor_item.end()) {
throw out_of_range("item not present");