#include "world_server.h"
#include "login_server.h"
#include "login_types.h"
#include "../common/ip_util.h"
#include "../common/strings.h"
#include "../common/repositories/login_world_servers_repository.h"
#include "../common/repositories/login_server_admins_repository.h"

extern LoginServer server;
extern Database    database;

WorldServer::WorldServer(std::shared_ptr<EQ::Net::ServertalkServerConnection> worldserver_connection)
{
	m_connection                   = worldserver_connection;
	m_zones_booted                 = 0;
	m_players_online               = 0;
	m_server_status                = 0;
	m_server_id                    = 0;
	m_server_list_type_id          = 0;
	m_server_process_type          = 0;
	m_is_server_authorized_to_list = false;
	m_is_server_trusted            = false;
	m_is_server_logged_in          = false;

	worldserver_connection->OnMessage(
		ServerOP_NewLSInfo,
		std::bind(&WorldServer::ProcessNewLSInfo, this, std::placeholders::_1, std::placeholders::_2)
	);

	worldserver_connection->OnMessage(
		ServerOP_LSStatus,
		std::bind(&WorldServer::ProcessLSStatus, this, std::placeholders::_1, std::placeholders::_2)
	);

	worldserver_connection->OnMessage(
		ServerOP_UsertoWorldRespLeg,
		std::bind(
			&WorldServer::ProcessUserToWorldResponseLegacy,
			this,
			std::placeholders::_1,
			std::placeholders::_2
		)
	);

	worldserver_connection->OnMessage(
		ServerOP_UsertoWorldResp,
		std::bind(&WorldServer::ProcessUserToWorldResponse, this, std::placeholders::_1, std::placeholders::_2)
	);

	worldserver_connection->OnMessage(
		ServerOP_LSAccountUpdate,
		std::bind(&WorldServer::ProcessLSAccountUpdate, this, std::placeholders::_1, std::placeholders::_2)
	);
}

WorldServer::~WorldServer() = default;

void WorldServer::Reset()
{
	m_server_id                    = 0;
	m_zones_booted                 = 0;
	m_players_online               = 0;
	m_server_status                = 0;
	m_server_list_type_id          = 0;
	m_server_process_type          = 0;
	m_is_server_authorized_to_list = false;
	m_is_server_logged_in          = false;
}

void WorldServer::ProcessNewLSInfo(uint16_t opcode, const EQ::Net::Packet &packet)
{
	LogNetcode(
		"Application packet received from server [{:#04x}] [Size: {}]\n{}",
		opcode, packet.Length(), packet.ToString()
	);

	if (packet.Length() < sizeof(LoginserverNewWorldRequest)) {
		LogError(
			"Received application packet with opcode ServerOP_NewLSInfo, but it was too small. Discarded to avoid buffer overrun."
		);
		return;
	}

	auto *r = (LoginserverNewWorldRequest *) packet.Data();

	// If remote IP is missing, use local IP unless it's 127.0.0.1
	if (r->remote_ip_address[0] == '\0' && r->local_ip_address[0] != '\0' &&
		strcmp(r->local_ip_address, "127.0.0.1") != 0) {
		strncpy(r->remote_ip_address, r->local_ip_address, sizeof(r->remote_ip_address) - 1);
		r->remote_ip_address[sizeof(r->remote_ip_address) - 1] = '\0'; // Ensure null termination
	}

	LogInfo(
		"New World Server Info | name [{}] shortname [{}] remote_address [{}] local_address [{}] account [{}] password [{}] server_type [{}]",
		r->server_long_name,
		r->server_short_name,
		r->remote_ip_address,
		r->local_ip_address,
		r->account_name,
		r->account_password,
		r->server_process_type
	);

	HandleNewWorldserver(r);
}

void WorldServer::ProcessLSStatus(uint16_t opcode, const EQ::Net::Packet &packet)
{
	LogNetcode(
		"Application packet received from server [{:#04x}] [Size: {}]\n{}",
		opcode,
		packet.Length(),
		packet.ToString()
	);

	if (packet.Length() < sizeof(LoginserverWorldStatusUpdate)) {
		LogError(
			"Received application packet from server that had opcode ServerOP_LSStatus, but was too small. Discarded to avoid buffer overrun"
		);

		return;
	}

	auto *ls_status = (LoginserverWorldStatusUpdate *) packet.Data();

	LogDebug(
		"World Server Status Update Received | Server [{}] Status [{}] Players [{}] Zones [{}]",
		m_server_long_name,
		ls_status->status,
		ls_status->num_players,
		ls_status->num_zones
	);

	HandleWorldserverStatusUpdate(ls_status);
}

void WorldServer::ProcessUserToWorldResponseLegacy(uint16_t opcode, const EQ::Net::Packet &packet)
{
	LogNetcode(
		"Application packet received from server [{:#04x}] [Size: {}]\n{}",
		opcode,
		packet.Length(),
		packet.ToString()
	);

	if (packet.Length() < sizeof(UsertoWorldResponseLegacy)) {
		LogError(
			"Received application packet from server that had opcode ServerOP_UsertoWorldResp, "
			"but was too small. Discarded to avoid buffer overrun"
		);

		return;
	}

	auto *res = (UsertoWorldResponseLegacy *) packet.Data();

	LogDebug("Trying to find client with user id of [{}]", res->lsaccountid);
	std::string db_loginserver = "local";
	if (std::getenv("LSPX")) {
		db_loginserver = "eqemu";
	}
	Client *c = server.client_manager->GetClient(res->lsaccountid, db_loginserver);
	if (c) {
		LogDebug(
			"Found client with user id of [{}] and account name of [{}]",
			res->lsaccountid,
			c->GetAccountName()
		);

		auto *outapp = new EQApplicationPacket(
			OP_PlayEverquestResponse,
			sizeof(PlayEverquestResponse)
		);

		auto *play = (PlayEverquestResponse *) outapp->pBuffer;
		play->base_header.sequence = c->GetCurrentPlaySequence();
		play->server_number        = c->GetSelectedPlayServerID();

		if (res->response > 0) {
			play->base_reply.success = true;
			SendClientAuthToWorld(c);
		}

		switch (res->response) {
			case UserToWorldStatusSuccess:
				play->base_reply.error_str_id = LS::ErrStr::ERROR_NONE;
				break;
			case UserToWorldStatusWorldUnavail:
				play->base_reply.error_str_id = LS::ErrStr::ERROR_SERVER_UNAVAILABLE;
				break;
			case UserToWorldStatusSuspended:
				play->base_reply.error_str_id = LS::ErrStr::ERROR_ACCOUNT_SUSPENDED;
				break;
			case UserToWorldStatusBanned:
				play->base_reply.error_str_id = LS::ErrStr::ERROR_ACCOUNT_BANNED;
				break;
			case UserToWorldStatusWorldAtCapacity:
				play->base_reply.error_str_id = LS::ErrStr::ERROR_WORLD_MAX_CAPACITY;
				break;
			case UserToWorldStatusAlreadyOnline:
				play->base_reply.error_str_id = LS::ErrStr::ERROR_ACTIVE_CHARACTER;
				break;
			default:
				play->base_reply.error_str_id = LS::ErrStr::ERROR_UNKNOWN;
				break;
		}

		LogDebug(
			"Sending play response: allowed [{}] sequence [{}] server number [{}] message [{}]",
			play->base_reply.success,
			play->base_header.sequence,
			play->server_number,
			play->base_reply.error_str_id
		);

		c->SendPlayResponse(outapp);
		delete outapp;
	}
	else {
		LogError(
			"Received User-To-World Response for [{}] but could not find the client referenced!",
			res->lsaccountid
		);
	}
}

void WorldServer::ProcessUserToWorldResponse(uint16_t opcode, const EQ::Net::Packet &packet)
{
	LogNetcode(
		"Application packet received from server [{:#04x}] [Size: {}]\n{}",
		opcode,
		packet.Length(),
		packet.ToString()
	);

	if (packet.Length() < sizeof(UsertoWorldResponse)) {
		LogError(
			"Received application packet from server that had opcode ServerOP_UsertoWorldResp, "
			"but was too small. Discarded to avoid buffer overrun"
		);

		return;
	}

	auto res = (UsertoWorldResponse *) packet.Data();
	LogDebug("Trying to find client with user id of [{}]", res->lsaccountid);

	Client *c = server.client_manager->GetClient(
		res->lsaccountid,
		res->login
	);

	if (c) {
		LogDebug(
			"Found client with user id of [{}] and account name of {}",
			res->lsaccountid,
			c->GetAccountName().c_str()
		);

		auto *outapp = new EQApplicationPacket(
			OP_PlayEverquestResponse,
			sizeof(PlayEverquestResponse)
		);

		auto *r = (PlayEverquestResponse *) outapp->pBuffer;
		r->base_header.sequence = c->GetCurrentPlaySequence();
		r->server_number        = c->GetSelectedPlayServerID();

		LogDebug(
			"Found sequence and play of [{}] [{}]",
			c->GetCurrentPlaySequence(),
			c->GetSelectedPlayServerID()
		);

		LogDebug("[Size: [{}]] {}", outapp->size, DumpPacketToString(outapp));

		if (res->response > 0) {
			r->base_reply.success = true;
			SendClientAuthToWorld(c);
		}

		switch (res->response) {
			case UserToWorldStatusSuccess:
				r->base_reply.error_str_id = LS::ErrStr::ERROR_NONE;
				break;
			case UserToWorldStatusWorldUnavail:
				r->base_reply.error_str_id = LS::ErrStr::ERROR_SERVER_UNAVAILABLE;
				break;
			case UserToWorldStatusSuspended:
				r->base_reply.error_str_id = LS::ErrStr::ERROR_ACCOUNT_SUSPENDED;
				break;
			case UserToWorldStatusBanned:
				r->base_reply.error_str_id = LS::ErrStr::ERROR_ACCOUNT_BANNED;
				break;
			case UserToWorldStatusWorldAtCapacity:
				r->base_reply.error_str_id = LS::ErrStr::ERROR_WORLD_MAX_CAPACITY;
				break;
			case UserToWorldStatusAlreadyOnline:
				r->base_reply.error_str_id = LS::ErrStr::ERROR_ACTIVE_CHARACTER;
				break;
			default:
				r->base_reply.error_str_id = LS::ErrStr::ERROR_UNKNOWN;
				break;
		}

		LogDebug(
			"Sending play response with following data, allowed [{}], sequence {}, server number {}, message {}",
			r->base_reply.success,
			r->base_header.sequence,
			r->server_number,
			r->base_reply.error_str_id
		);

		c->SendPlayResponse(outapp);
		delete outapp;
	}
	else {
		LogError(
			"Received User-To-World Response for [{}] but could not find the client referenced!.",
			res->lsaccountid
		);
	}
}

void WorldServer::ProcessLSAccountUpdate(uint16_t opcode, const EQ::Net::Packet &packet)
{
	LogNetcode(
		"Application packet received from server [{:#04x}] [Size: {}]\n{}",
		opcode,
		packet.Length(),
		packet.ToString()
	);

	if (packet.Length() < sizeof(LoginserverAccountUpdate)) {
		LogError(
			"Received application packet from server that had opcode ServerLSAccountUpdate_Struct, "
			"but was too small. Discarded to avoid buffer overrun"
		);

		return;
	}

	LogDebug("ServerOP_LSAccountUpdate packet received from [{}]", m_server_short_name);

	auto *r = (LoginserverAccountUpdate *) packet.Data();
	if (m_is_server_trusted) {
		LogDebug("ServerOP_LSAccountUpdate update processed for: [{}]", r->user_account_name);

		LoginAccountContext c{};
		c.username           = r->user_account_name;
		c.source_loginserver = "local";
		auto a = LoginAccountsRepository::GetAccountFromContext(database, c);
		if (a.id > 0) {
			a.account_email    = r->user_email;
			a.account_password = r->user_account_password;
			a.last_ip_address  = "0.0.0.0";
			LoginAccountsRepository::UpdateOne(database, a);
		}
	}
}

void WorldServer::HandleNewWorldserver(LoginserverNewWorldRequest *req)
{
	if (m_is_server_logged_in) {
		LogInfo("Login server was already marked as logged in, returning");
		return;
	}

	if (!HandleNewWorldserverValidation(req)) {
		LogError("failed validation rules");
		return;
	}

	SanitizeWorldServerName(req->server_long_name);

	m_server_long_name    = req->server_long_name;
	m_server_short_name   = req->server_short_name;
	m_account_password    = req->account_password;
	m_account_name        = req->account_name;
	m_local_ip            = req->local_ip_address;
	m_remote_ip_address   = req->remote_ip_address;
	m_server_version      = req->server_version;
	m_protocol            = req->protocol_version;
	m_server_process_type = req->server_process_type;
	m_is_server_logged_in = true;

	// Handle Duplicate Servers
	if (server.server_manager->DoesServerExist(m_server_long_name, m_server_short_name, this)) {
		if (server.options.IsRejectingDuplicateServers()) {
			LogError("World tried to login but a server with that name already exists");
			return;
		}
		LogInfo("World tried to login but a server with that name already exists, destroying [{}]", m_server_long_name);
		server.server_manager->DestroyServerByName(m_server_long_name, m_server_short_name, this);
	}

	LoginWorldContext c;
	c.long_name  = m_server_long_name;
	c.short_name = m_server_short_name;

	LoginServerAdminsRepository::LoginServerAdmins admin;

	// Handle Admin Authentication
	if (!m_account_name.empty() && !m_account_password.empty()) {
		admin = LoginServerAdminsRepository::GetByName(database, m_account_name);

		LoginWorldAdminAccountContext ac;
		ac.id            = admin.id;
		ac.username      = m_account_name;
		ac.password      = m_account_password;
		ac.password_hash = admin.account_password;

		if (admin.id && WorldServer::ValidateWorldServerAdminLogin(ac, admin)) {
			LogDebug(
				"Authenticated world admin [{}] ({}) for world [{}]",
				m_account_name,
				admin.id,
				m_server_short_name
			);
			c.admin_id = admin.id;
			m_is_server_authorized_to_list = true;
		}
	}

	auto world = LoginWorldServersRepository::GetFromWorldContext(database, c);
	if (!world.id) {
		if (!server.options.IsUnregisteredAllowed()) {
			LogError("WorldServer [{}] is not registered, and unregistered servers are not allowed",
					 m_server_long_name);
			return;
		}

		LogInfo("Server [{}] is not registered, handling as unregistered", m_server_long_name);
		m_is_server_authorized_to_list = true;

		auto w = LoginWorldServersRepository::NewEntity();
		w.long_name                 = m_server_long_name;
		w.short_name                = m_server_short_name;
		w.last_ip_address           = m_remote_ip_address;
		w.login_server_list_type_id = LS::ServerType::Standard;
		w.last_login_date           = std::time(nullptr);
		auto created = LoginWorldServersRepository::InsertOne(database, w);
		if (!created.id) {
			LogError("Failed to auto-register world server [{}]", m_server_long_name);
			return;
		}

		LogInfo(
			"Auto-registered world server [{}] with ID [{}]",
			m_server_long_name,
			created.id
		);
	}
	else {
		m_server_description           = world.tag_description;
		m_server_id                    = world.id;
		m_is_server_trusted            = world.is_server_trusted;
		m_server_list_type_id          = world.login_server_list_type_id;
		m_is_server_authorized_to_list = true;

		LogInfo(
			"Server ID [{}] long_name [{}] short_name [{}] successfully authenticated",
			world.id,
			world.long_name,
			world.short_name
		);
	}

	LogInfo(
		"World registration id [{}] for server [{}] ip_address [{}]",
		m_server_id,
		m_server_long_name,
		m_remote_ip_address
	);

	// Update the last login date and IP address
	world.last_login_date = std::time(nullptr);
	world.last_ip_address = m_remote_ip_address;
	LoginWorldServersRepository::UpdateOne(database, world);

	WorldServer::FormatWorldServerName(
		req->server_long_name,
		m_server_list_type_id
	);

	m_server_long_name = req->server_long_name;
}

void WorldServer::HandleWorldserverStatusUpdate(LoginserverWorldStatusUpdate *u)
{
	m_players_online = u->num_players;
	m_zones_booted   = u->num_zones;
	m_server_status  = u->status;
}

void WorldServer::SendClientAuthToWorld(Client *c)
{
	EQ::Net::DynamicPacket outapp;
	ClientAuth             a{};

	a.loginserver_account_id = c->GetAccountID();

	strncpy(a.account_name, c->GetAccountName().c_str(), 30);
	strncpy(a.key, c->GetLoginKey().c_str(), 30);

	a.lsadmin        = 0;
	a.is_world_admin = 0;
	a.ip_address     = inet_addr(c->GetConnection()->GetRemoteAddr().c_str());
	strncpy(a.loginserver_name, &c->GetLoginServerName()[0], 64);

	const std::string &client_address(c->GetConnection()->GetRemoteAddr());
	std::string       world_address(m_connection->Handle()->RemoteIP());

	if (client_address == world_address) {
		a.is_client_from_local_network = 1;
	}
	else if (IpUtil::IsIpInPrivateRfc1918(client_address)) {
		LogInfo("Client is authenticating from a local address [{}]", client_address);
		a.is_client_from_local_network = 1;
	}
	else {
		a.is_client_from_local_network = 0;
	}

	struct in_addr ip_addr{};
	ip_addr.s_addr = a.ip_address;

	LogInfo(
		"Client authentication response: world_address [{}] client_address [{}]",
		world_address,
		client_address
	);

	LogInfo(
		"Sending Client Authentication Response ls_account_id [{}] ls_name [{}] name [{}] key [{}] ls_admin [{}] "
		"world_admin [{}] ip [{}] local [{}]",
		a.loginserver_account_id,
		a.loginserver_name,
		a.account_name,
		a.key,
		a.lsadmin,
		a.is_world_admin,
		inet_ntoa(ip_addr),
		a.is_client_from_local_network
	);

	outapp.PutSerialize(0, a);
	m_connection->Send(ServerOP_LSClientAuth, outapp);

	LogNetcode(
		"Sending [{:#04x}] [Size: {}]\n{}",
		ServerOP_LSClientAuth,
		outapp.Length(),
		outapp.ToString()
	);
}

constexpr static int MAX_ACCOUNT_NAME_LENGTH          = 30;
constexpr static int MAX_ACCOUNT_PASSWORD_LENGTH      = 30;
constexpr static int MAX_SERVER_LONG_NAME_LENGTH      = 200;
constexpr static int MAX_SERVER_SHORT_NAME_LENGTH     = 50;
constexpr static int MAX_SERVER_LOCAL_ADDRESS_LENGTH  = 125;
constexpr static int MAX_SERVER_REMOTE_ADDRESS_LENGTH = 125;
constexpr static int MAX_SERVER_VERSION_LENGTH        = 64;
constexpr static int MAX_SERVER_PROTOCOL_VERSION      = 25;

bool WorldServer::HandleNewWorldserverValidation(LoginserverNewWorldRequest *r)
{
	if (strlen(r->account_name) >= MAX_ACCOUNT_NAME_LENGTH) {
		LogError("HandleNewWorldserver error [account_name] was too long | max [{}]", MAX_ACCOUNT_NAME_LENGTH);
		return false;
	}
	else if (strlen(r->account_password) >= MAX_ACCOUNT_PASSWORD_LENGTH) {
		LogError("HandleNewWorldserver error [account_password] was too long | max [{}]", MAX_ACCOUNT_PASSWORD_LENGTH);
		return false;
	}
	else if (strlen(r->server_long_name) >= MAX_SERVER_LONG_NAME_LENGTH) {
		LogError("HandleNewWorldserver error [server_long_name] was too long | max [{}]", MAX_SERVER_LONG_NAME_LENGTH);
		return false;
	}
	else if (strlen(r->server_short_name) >= MAX_SERVER_SHORT_NAME_LENGTH) {
		LogError("HandleNewWorldserver error [server_short_name] was too long | max [{}]",
				 MAX_SERVER_SHORT_NAME_LENGTH);
		return false;
	}
	else if (strlen(r->server_version) >= MAX_SERVER_VERSION_LENGTH) {
		LogError("HandleNewWorldserver error [server_version] was too long | max [{}]", MAX_SERVER_VERSION_LENGTH);
		return false;
	}
	else if (strlen(r->protocol_version) >= MAX_SERVER_PROTOCOL_VERSION) {
		LogError("HandleNewWorldserver error [protocol_version] was too long | max [{}]", MAX_SERVER_PROTOCOL_VERSION);
		return false;
	}

	if (strlen(r->local_ip_address) <= MAX_SERVER_LOCAL_ADDRESS_LENGTH) {
		if (strlen(r->local_ip_address) == 0) {
			LogError("HandleNewWorldserver error, local address was null, defaulting to localhost");
			m_local_ip = "127.0.0.1";
		}
		else {
			m_local_ip = r->local_ip_address;
		}
	}
	else {
		LogError("HandleNewWorldserver error, local address was too long | max [{}]", MAX_SERVER_LOCAL_ADDRESS_LENGTH);
		return false;
	}

	if (strlen(r->remote_ip_address) <= MAX_SERVER_REMOTE_ADDRESS_LENGTH) {
		if (strlen(r->remote_ip_address) == 0) {
			m_remote_ip_address = GetConnection()->Handle()->RemoteIP();

			LogWarning(
				"Remote address was null, defaulting to stream address [{}]",
				m_remote_ip_address
			);
		}
		else {
			m_remote_ip_address = r->remote_ip_address;
		}
	}
	else {
		m_remote_ip_address = GetConnection()->Handle()->RemoteIP();

		LogWarning(
			"HandleNewWorldserver remote address was too long, defaulting to stream address [{}]",
			m_remote_ip_address
		);
	}

	return true;
}

bool WorldServer::ValidateWorldServerAdminLogin(
	LoginWorldAdminAccountContext &c,
	LoginServerAdminsRepository::LoginServerAdmins &admin
)
{
	auto encryption_mode = server.options.GetEncryptionMode();
	if (eqcrypt_verify_hash(c.username, c.password, c.password_hash, encryption_mode)) {
		return true;
	}

	if (encryption_mode < EncryptionModeArgon2) {
		encryption_mode = EncryptionModeArgon2;
	}

	uint32 insecure_source_encryption_mode = 0;
	auto   verify_encryption               = [&](int start, int end) {
		for (int i = start; i <= end; ++i) {
			if (i != encryption_mode && eqcrypt_verify_hash(c.username, c.password, c.password_hash, i)) {
				LogDebug("Checking for [{}] world admin", GetEncryptionByModeId(i));
				insecure_source_encryption_mode = i;
			}
		}
	};

	switch (c.password_hash.length()) {
		case CryptoHash::md5_hash_length:
			verify_encryption(EncryptionModeMD5, EncryptionModeMD5Triple);
			break;
		case CryptoHash::sha1_hash_length:
			verify_encryption(EncryptionModeSHA, EncryptionModeSHATriple);
			break;
		case CryptoHash::sha512_hash_length:
			verify_encryption(EncryptionModeSHA512, EncryptionModeSHA512Triple);
			break;
	}

	if (insecure_source_encryption_mode > 0) {
		LogInfo(
			"Updated insecure world_admin_username [{}] from mode [{}] ({}) to mode [{}] ({})",
			c.username,
			GetEncryptionByModeId(insecure_source_encryption_mode),
			insecure_source_encryption_mode,
			GetEncryptionByModeId(encryption_mode),
			encryption_mode
		);

		admin.account_password = eqcrypt_hash(c.username, c.password, encryption_mode);
		LoginServerAdminsRepository::UpdateOne(database, admin);

		return true;
	}

	return false;
}

void WorldServer::SerializeForClientServerList(SerializeBuffer &out, bool use_local_ip, LSClientVersion version) const
{
	// see LoginClientServerData_Struct
	if (use_local_ip) {
		out.WriteString(GetLocalIP());
	}
	else {
		out.WriteString(m_remote_ip_address);
	}

	if (version == cv_larion) {
		out.WriteUInt32(9000);
	}

	switch (GetServerListID()) {
		case LS::ServerType::Legends:
			out.WriteInt32(LS::ServerTypeFlags::Legends);
			break;
		case LS::ServerType::Preferred:
			out.WriteInt32(LS::ServerTypeFlags::Preferred);
			break;
		default:
			out.WriteInt32(LS::ServerTypeFlags::Standard);
			break;
	}
	if (version == cv_larion) {
		auto server_id = m_server_id;
		//if this is 0, the client will not show the server in the list
		out.WriteUInt32(1);
		out.WriteUInt32(server_id);
	}
	else {
		out.WriteUInt32(m_server_id);
	}

	out.WriteString(m_server_long_name);
	out.WriteString("us"); // country code
	out.WriteString("en"); // language code

	// 0 = Up, 1 = Down, 2 = Up, 3 = down, 4 = locked, 5 = locked(down)
	if (GetStatus() < 0) {
		if (GetZonesBooted() == 0) {
			out.WriteInt32(LS::ServerStatusFlags::Down);
		}
		else {
			out.WriteInt32(LS::ServerStatusFlags::Locked);
		}
	}
	else {
		out.WriteInt32(LS::ServerStatusFlags::Up);
	}

	out.WriteUInt32(GetPlayersOnline());
}

void WorldServer::FormatWorldServerName(char *name, int8 server_list_type)
{
	std::string server_long_name = name;
	server_long_name = Strings::Trim(server_long_name);

	bool name_set_to_bottom = false;
	if (server_list_type == LS::ServerType::Standard) {
		if (server.options.IsWorldDevTestServersListBottom()) {
			std::string s = Strings::ToLower(server_long_name);
			if (s.find("dev") != std::string::npos) {
				server_long_name   = fmt::format("|D| {}", server_long_name);
				name_set_to_bottom = true;
			}
			else if (s.find("test") != std::string::npos) {
				server_long_name   = fmt::format("|T| {}", server_long_name);
				name_set_to_bottom = true;
			}
			else if (s.find("installer") != std::string::npos) {
				server_long_name   = fmt::format("|I| {}", server_long_name);
				name_set_to_bottom = true;
			}
		}
		if (server.options.IsWorldSpecialCharacterStartListBottom() && !name_set_to_bottom) {
			auto first_char = server_long_name.c_str()[0];
			if (IsAllowedWorldServerCharacterList(first_char) && first_char != '|') {
				server_long_name   = fmt::format("|*| {}", server_long_name);
				name_set_to_bottom = true;
			}
		}
	}

	strn0cpy(name, server_long_name.c_str(), 201);
}
