eqemu-server/loginserver/server_manager.cpp

343 lines
9.0 KiB
C++

/**
* EQEmulator: Everquest Server Emulator
* Copyright (C) 2001-2019 EQEmulator Development Team (https://github.com/EQEmu/Server)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY except by those people which sell it, which
* are required to give you total support for your newly bought product;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "server_manager.h"
#include "login_server.h"
#include "login_structures.h"
#include <stdlib.h>
#include "../common/eqemu_logsys.h"
#include "../common/ip_util.h"
extern LoginServer server;
extern bool run_server;
ServerManager::ServerManager()
{
int listen_port = server.config.GetVariableInt("general", "listen_port", 5998);
server_connection.reset(new EQ::Net::ServertalkServer());
EQ::Net::ServertalkServerOptions opts;
opts.port = listen_port;
opts.ipv6 = false;
server_connection->Listen(opts);
LogInfo("Loginserver now listening on port [{0}]", listen_port);
server_connection->OnConnectionIdentified(
"World", [this](std::shared_ptr<EQ::Net::ServertalkServerConnection> world_connection) {
LogInfo(
"New world server connection from {0}:{1}",
world_connection->Handle()->RemoteIP(),
world_connection->Handle()->RemotePort()
);
auto iter = world_servers.begin();
while (iter != world_servers.end()) {
if ((*iter)->GetConnection()->Handle()->RemoteIP().compare(world_connection->Handle()->RemoteIP()) ==
0 &&
(*iter)->GetConnection()->Handle()->RemotePort() == world_connection->Handle()->RemotePort()) {
LogInfo(
"World server already existed for {0}:{1}, removing existing connection.",
world_connection->Handle()->RemoteIP(),
world_connection->Handle()->RemotePort()
);
world_servers.erase(iter);
break;
}
++iter;
}
world_servers.push_back(std::unique_ptr<WorldServer>(new WorldServer(world_connection)));
}
);
server_connection->OnConnectionRemoved(
"World", [this](std::shared_ptr<EQ::Net::ServertalkServerConnection> c) {
auto iter = world_servers.begin();
while (iter != world_servers.end()) {
if ((*iter)->GetConnection()->GetUUID() == c->GetUUID()) {
LogF(Logs::General,
Logs::World_Server,
"World server {0} has been disconnected, removing.",
(*iter)->GetLongName().c_str());
world_servers.erase(iter);
return;
}
++iter;
}
}
);
}
ServerManager::~ServerManager() = default;
/**
* @param ip_address
* @param port
* @return
*/
WorldServer *ServerManager::GetServerByAddress(const std::string &ip_address, int port)
{
auto iter = world_servers.begin();
while (iter != world_servers.end()) {
if ((*iter)->GetConnection()->Handle()->RemoteIP() == ip_address &&
(*iter)->GetConnection()->Handle()->RemotePort()) {
return (*iter).get();
}
++iter;
}
return nullptr;
}
/**
* @param client
* @param sequence
* @return
*/
EQApplicationPacket *ServerManager::CreateServerListPacket(Client *client, uint32 sequence)
{
unsigned int packet_size = sizeof(ServerListHeader_Struct);
unsigned int server_count = 0;
in_addr in;
in.s_addr = client->GetConnection()->GetRemoteIP();
std::string client_ip = inet_ntoa(in);
auto iter = world_servers.begin();
while (iter != world_servers.end()) {
if ((*iter)->IsAuthorized() == false) {
++iter;
continue;
}
std::string world_ip = (*iter)->GetConnection()->Handle()->RemoteIP();
if (world_ip.compare(client_ip) == 0) {
packet_size += (*iter)->GetLongName().size() + (*iter)->GetLocalIP().size() + 24;
}
else if (IpUtil::IsIpInPrivateRfc1918(client_ip)) {
LogInfo("Client is requesting server list from a local address [{0}]", client_ip);
packet_size += (*iter)->GetLongName().size() + (*iter)->GetLocalIP().size() + 24;
}
else {
packet_size += (*iter)->GetLongName().size() + (*iter)->GetRemoteIP().size() + 24;
}
server_count++;
++iter;
}
auto *outapp = new EQApplicationPacket(OP_ServerListResponse, packet_size);
auto *server_list = (ServerListHeader_Struct *) outapp->pBuffer;
server_list->Unknown1 = sequence;
server_list->Unknown2 = 0x00000000;
server_list->Unknown3 = 0x01650000;
/**
* Not sure what this is but it should be noted setting it to
* 0xFFFFFFFF crashes the client so: don't do that.
*/
server_list->Unknown4 = 0x00000000;
server_list->NumberOfServers = server_count;
unsigned char *data_pointer = outapp->pBuffer;
data_pointer += sizeof(ServerListHeader_Struct);
iter = world_servers.begin();
while (iter != world_servers.end()) {
if ((*iter)->IsAuthorized() == false) {
++iter;
continue;
}
std::string world_ip = (*iter)->GetConnection()->Handle()->RemoteIP();
if (world_ip.compare(client_ip) == 0) {
memcpy(data_pointer, (*iter)->GetLocalIP().c_str(), (*iter)->GetLocalIP().size());
data_pointer += ((*iter)->GetLocalIP().size() + 1);
}
else if (IpUtil::IsIpInPrivateRfc1918(client_ip)) {
memcpy(data_pointer, (*iter)->GetLocalIP().c_str(), (*iter)->GetLocalIP().size());
data_pointer += ((*iter)->GetLocalIP().size() + 1);
}
else {
memcpy(data_pointer, (*iter)->GetRemoteIP().c_str(), (*iter)->GetRemoteIP().size());
data_pointer += ((*iter)->GetRemoteIP().size() + 1);
}
switch ((*iter)->GetServerListID()) {
case 1: {
*(unsigned int *) data_pointer = 0x00000030;
break;
}
case 2: {
*(unsigned int *) data_pointer = 0x00000009;
break;
}
default: {
*(unsigned int *) data_pointer = 0x00000001;
}
}
data_pointer += 4;
*(unsigned int *) data_pointer = (*iter)->GetRuntimeID();
data_pointer += 4;
memcpy(data_pointer, (*iter)->GetLongName().c_str(), (*iter)->GetLongName().size());
data_pointer += ((*iter)->GetLongName().size() + 1);
memcpy(data_pointer, "EN", 2);
data_pointer += 3;
memcpy(data_pointer, "US", 2);
data_pointer += 3;
// 0 = Up, 1 = Down, 2 = Up, 3 = down, 4 = locked, 5 = locked(down)
if ((*iter)->GetStatus() < 0) {
if ((*iter)->GetZonesBooted() == 0) {
*(uint32 *) data_pointer = 0x01;
}
else {
*(uint32 *) data_pointer = 0x04;
}
}
else {
*(uint32 *) data_pointer = 0x02;
}
data_pointer += 4;
*(uint32 *) data_pointer = (*iter)->GetPlayersOnline();
data_pointer += 4;
++iter;
}
return outapp;
}
/**
* @param server_id
* @param client_account_id
* @param client_loginserver
*/
void ServerManager::SendUserToWorldRequest(
unsigned int server_id,
unsigned int client_account_id,
const std::string &client_loginserver
)
{
auto iter = world_servers.begin();
bool found = false;
while (iter != world_servers.end()) {
if ((*iter)->GetRuntimeID() == server_id) {
EQ::Net::DynamicPacket outapp;
outapp.Resize(sizeof(UsertoWorldRequest_Struct));
auto *user_to_world_request = (UsertoWorldRequest_Struct *) outapp.Data();
user_to_world_request->worldid = server_id;
user_to_world_request->lsaccountid = client_account_id;
strncpy(user_to_world_request->login, &client_loginserver[0], 64);
(*iter)->GetConnection()->Send(ServerOP_UsertoWorldReq, outapp);
found = true;
if (server.options.IsDumpInPacketsOn()) {
LogInfo("{0}", outapp.ToString());
}
}
++iter;
}
if (!found && server.options.IsTraceOn()) {
LogError("Client requested a user to world but supplied an invalid id of {0}", server_id);
}
}
/**
* @param server_long_name
* @param server_short_name
* @param ignore
* @return
*/
bool ServerManager::ServerExists(
std::string server_long_name,
std::string server_short_name,
WorldServer *ignore
)
{
auto iter = world_servers.begin();
while (iter != world_servers.end()) {
if ((*iter).get() == ignore) {
++iter;
continue;
}
if ((*iter)->GetLongName().compare(server_long_name) == 0 &&
(*iter)->GetShortName().compare(server_short_name) == 0) {
return true;
}
++iter;
}
return false;
}
/**
* @param server_long_name
* @param server_short_name
* @param ignore
*/
void ServerManager::DestroyServerByName(
std::string server_long_name,
std::string server_short_name,
WorldServer *ignore
)
{
auto iter = world_servers.begin();
while (iter != world_servers.end()) {
if ((*iter).get() == ignore) {
++iter;
continue;
}
if ((*iter)->GetLongName().compare(server_long_name) == 0 &&
(*iter)->GetShortName().compare(server_short_name) == 0) {
(*iter)->GetConnection()->Handle()->Disconnect();
iter = world_servers.erase(iter);
continue;
}
++iter;
}
}
/**
* @return
*/
const std::list<std::unique_ptr<WorldServer>> &ServerManager::getWorldServers() const
{
return world_servers;
}