mirror of https://github.com/OpenRCT2/OpenRCT2.git
Merge pull request #11511 from IntelOrca/plugin/improve-network-apis
Improve network plugin APIs
This commit is contained in:
commit
76f41285b4
|
@ -185,6 +185,7 @@ declare global {
|
|||
subscribe(hook: "interval.tick", callback: () => void): IDisposable;
|
||||
subscribe(hook: "interval.day", callback: () => void): IDisposable;
|
||||
subscribe(hook: "network.chat", callback: (e: NetworkChatEventArgs) => void): IDisposable;
|
||||
subscribe(hook: "network.authenticate", callback: (e: NetworkAuthenticateEventArgs) => void): IDisposable;
|
||||
subscribe(hook: "network.join", callback: (e: NetworkEventArgs) => void): IDisposable;
|
||||
subscribe(hook: "network.leave", callback: (e: NetworkEventArgs) => void): IDisposable;
|
||||
}
|
||||
|
@ -283,6 +284,13 @@ declare global {
|
|||
message: string;
|
||||
}
|
||||
|
||||
interface NetworkAuthenticateEventArgs {
|
||||
readonly name: number;
|
||||
readonly ipAddress: string;
|
||||
readonly publicKeyHash: string;
|
||||
cancel: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* APIs for the in-game date.
|
||||
*/
|
||||
|
@ -588,13 +596,16 @@ declare global {
|
|||
*/
|
||||
interface Network {
|
||||
readonly mode: NetworkMode;
|
||||
readonly groups: number;
|
||||
readonly players: number;
|
||||
readonly numGroups: number;
|
||||
readonly numPlayers: number;
|
||||
readonly groups: PlayerGroup[];
|
||||
readonly players: Player[];
|
||||
defaultGroup: number;
|
||||
|
||||
getServerInfo(): ServerInfo;
|
||||
addGroup(): void;
|
||||
getGroup(index: number): PlayerGroup;
|
||||
setGroups(groups: PlayerGroup[]): void;
|
||||
removeGroup(index: number): void;
|
||||
getPlayer(index: number): Player;
|
||||
kickPlayer(index: number): void;
|
||||
sendMessage(message: string): void;
|
||||
|
@ -613,6 +624,8 @@ declare global {
|
|||
readonly ping: number;
|
||||
readonly commandsRan: number;
|
||||
readonly moneySpent: number;
|
||||
readonly ipAddress: string;
|
||||
readonly publicKeyHash: string;
|
||||
}
|
||||
|
||||
interface PlayerGroup {
|
||||
|
|
|
@ -136,6 +136,7 @@ public:
|
|||
void ProcessDisconnectedClients();
|
||||
std::vector<std::unique_ptr<NetworkPlayer>>::iterator GetPlayerIteratorByID(uint8_t id);
|
||||
NetworkPlayer* GetPlayerByID(uint8_t id);
|
||||
NetworkConnection* GetPlayerConnection(uint8_t id);
|
||||
std::vector<std::unique_ptr<NetworkGroup>>::iterator GetGroupIteratorByID(uint8_t id);
|
||||
NetworkGroup* GetGroupByID(uint8_t id);
|
||||
static const char* FormatChat(NetworkPlayer* fromplayer, const char* text);
|
||||
|
@ -176,7 +177,7 @@ public:
|
|||
void Server_Send_TOKEN(NetworkConnection& connection);
|
||||
void Server_Send_MAP(NetworkConnection* connection = nullptr);
|
||||
void Client_Send_CHAT(const char* text);
|
||||
void Server_Send_CHAT(const char* text);
|
||||
void Server_Send_CHAT(const char* text, const std::vector<uint8_t>& playerIds = {});
|
||||
void Client_Send_GAME_ACTION(const GameAction* action);
|
||||
void Server_Send_GAME_ACTION(const GameAction* action);
|
||||
void Server_Send_TICK();
|
||||
|
@ -665,6 +666,19 @@ uint8_t Network::GetPlayerID()
|
|||
return player_id;
|
||||
}
|
||||
|
||||
NetworkConnection* Network::GetPlayerConnection(uint8_t id)
|
||||
{
|
||||
auto player = GetPlayerByID(id);
|
||||
if (player != nullptr)
|
||||
{
|
||||
auto clientIt = std::find_if(
|
||||
client_connection_list.begin(), client_connection_list.end(),
|
||||
[player](const auto& conn) -> bool { return conn->Player == player; });
|
||||
return clientIt != client_connection_list.end() ? clientIt->get() : nullptr;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Network::Update()
|
||||
{
|
||||
_closeLock = true;
|
||||
|
@ -1654,12 +1668,28 @@ void Network::Client_Send_CHAT(const char* text)
|
|||
_serverConnection->QueuePacket(std::move(packet));
|
||||
}
|
||||
|
||||
void Network::Server_Send_CHAT(const char* text)
|
||||
void Network::Server_Send_CHAT(const char* text, const std::vector<uint8_t>& playerIds)
|
||||
{
|
||||
std::unique_ptr<NetworkPacket> packet(NetworkPacket::Allocate());
|
||||
*packet << static_cast<uint32_t>(NETWORK_COMMAND_CHAT);
|
||||
packet->WriteString(text);
|
||||
SendPacketToClients(*packet);
|
||||
|
||||
if (playerIds.empty())
|
||||
{
|
||||
// Empty players / default value means send to all players
|
||||
SendPacketToClients(*packet);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (auto playerId : playerIds)
|
||||
{
|
||||
auto conn = GetPlayerConnection(playerId);
|
||||
if (conn != nullptr && !conn->IsDisconnected)
|
||||
{
|
||||
conn->QueuePacket(NetworkPacket::Duplicate(*packet));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Network::Client_Send_GAME_ACTION(const GameAction* action)
|
||||
|
@ -1942,6 +1972,80 @@ void Network::ProcessPending()
|
|||
ProcessPlayerList();
|
||||
}
|
||||
|
||||
static bool ProcessPlayerAuthenticatePluginHooks(
|
||||
const NetworkConnection& connection, const std::string_view& name, const std::string_view& publicKeyHash)
|
||||
{
|
||||
# ifdef ENABLE_SCRIPTING
|
||||
using namespace OpenRCT2::Scripting;
|
||||
|
||||
auto& hookEngine = GetContext()->GetScriptEngine().GetHookEngine();
|
||||
if (hookEngine.HasSubscriptions(OpenRCT2::Scripting::HOOK_TYPE::NETWORK_AUTHENTICATE))
|
||||
{
|
||||
auto ctx = GetContext()->GetScriptEngine().GetContext();
|
||||
|
||||
// Create event args object
|
||||
DukObject eObj(ctx);
|
||||
eObj.Set("name", name);
|
||||
eObj.Set("publicKeyHash", publicKeyHash);
|
||||
eObj.Set("ipAddress", connection.Socket->GetIpAddress());
|
||||
eObj.Set("cancel", false);
|
||||
auto e = eObj.Take();
|
||||
|
||||
// Call the subscriptions
|
||||
hookEngine.Call(OpenRCT2::Scripting::HOOK_TYPE::NETWORK_AUTHENTICATE, e, false);
|
||||
|
||||
// Check if any hook has cancelled the join
|
||||
if (AsOrDefault(e["cancel"], false))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
# endif
|
||||
return true;
|
||||
}
|
||||
|
||||
static void ProcessPlayerJoinedPluginHooks(uint8_t playerId)
|
||||
{
|
||||
# ifdef ENABLE_SCRIPTING
|
||||
using namespace OpenRCT2::Scripting;
|
||||
|
||||
auto& hookEngine = GetContext()->GetScriptEngine().GetHookEngine();
|
||||
if (hookEngine.HasSubscriptions(OpenRCT2::Scripting::HOOK_TYPE::NETWORK_JOIN))
|
||||
{
|
||||
auto ctx = GetContext()->GetScriptEngine().GetContext();
|
||||
|
||||
// Create event args object
|
||||
DukObject eObj(ctx);
|
||||
eObj.Set("player", playerId);
|
||||
auto e = eObj.Take();
|
||||
|
||||
// Call the subscriptions
|
||||
hookEngine.Call(OpenRCT2::Scripting::HOOK_TYPE::NETWORK_JOIN, e, false);
|
||||
}
|
||||
# endif
|
||||
}
|
||||
|
||||
static void ProcessPlayerLeftPluginHooks(uint8_t playerId)
|
||||
{
|
||||
# ifdef ENABLE_SCRIPTING
|
||||
using namespace OpenRCT2::Scripting;
|
||||
|
||||
auto& hookEngine = GetContext()->GetScriptEngine().GetHookEngine();
|
||||
if (hookEngine.HasSubscriptions(OpenRCT2::Scripting::HOOK_TYPE::NETWORK_LEAVE))
|
||||
{
|
||||
auto ctx = GetContext()->GetScriptEngine().GetContext();
|
||||
|
||||
// Create event args object
|
||||
DukObject eObj(ctx);
|
||||
eObj.Set("player", playerId);
|
||||
auto e = eObj.Take();
|
||||
|
||||
// Call the subscriptions
|
||||
hookEngine.Call(OpenRCT2::Scripting::HOOK_TYPE::NETWORK_LEAVE, e, false);
|
||||
}
|
||||
# endif
|
||||
}
|
||||
|
||||
void Network::ProcessPlayerList()
|
||||
{
|
||||
if (GetMode() == NETWORK_MODE_SERVER)
|
||||
|
@ -1966,6 +2070,8 @@ void Network::ProcessPlayerList()
|
|||
|
||||
// List of active players found in the list.
|
||||
std::vector<uint8_t> activePlayerIds;
|
||||
std::vector<uint8_t> newPlayers;
|
||||
std::vector<uint8_t> removedPlayers;
|
||||
|
||||
for (auto&& pendingPlayer : itPending->second.players)
|
||||
{
|
||||
|
@ -1984,6 +2090,8 @@ void Network::ProcessPlayerList()
|
|||
_serverConnection->Player = player;
|
||||
}
|
||||
}
|
||||
|
||||
newPlayers.push_back(player->Id);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1993,19 +2101,35 @@ void Network::ProcessPlayerList()
|
|||
}
|
||||
|
||||
// Remove any players that are not in newly received list
|
||||
auto it = player_list.begin();
|
||||
while (it != player_list.end())
|
||||
for (const auto& player : player_list)
|
||||
{
|
||||
if (std::find(activePlayerIds.begin(), activePlayerIds.end(), (*it)->Id) == activePlayerIds.end())
|
||||
if (std::find(activePlayerIds.begin(), activePlayerIds.end(), player->Id) == activePlayerIds.end())
|
||||
{
|
||||
it = player_list.erase(it);
|
||||
}
|
||||
else
|
||||
{
|
||||
it++;
|
||||
removedPlayers.push_back(player->Id);
|
||||
}
|
||||
}
|
||||
|
||||
// Run player removed hooks (must be before players removed from list)
|
||||
for (auto playerId : removedPlayers)
|
||||
{
|
||||
ProcessPlayerLeftPluginHooks(playerId);
|
||||
}
|
||||
|
||||
// Run player joined hooks (must be after players added to list)
|
||||
for (auto playerId : newPlayers)
|
||||
{
|
||||
ProcessPlayerJoinedPluginHooks(playerId);
|
||||
}
|
||||
|
||||
// Now actually remove removed players from player list
|
||||
player_list.erase(
|
||||
std::remove_if(
|
||||
player_list.begin(), player_list.end(),
|
||||
[&removedPlayers](const std::unique_ptr<NetworkPlayer>& player) {
|
||||
return std::find(removedPlayers.begin(), removedPlayers.end(), player->Id) != removedPlayers.end();
|
||||
}),
|
||||
player_list.end());
|
||||
|
||||
_pendingPlayerLists.erase(itPending);
|
||||
itPending = _pendingPlayerLists.begin();
|
||||
}
|
||||
|
@ -2100,6 +2224,8 @@ void Network::ServerClientDisconnected(std::unique_ptr<NetworkConnection>& conne
|
|||
|
||||
// Log player disconnected event
|
||||
AppendServerLog(text);
|
||||
|
||||
ProcessPlayerLeftPluginHooks(connection_player->Id);
|
||||
}
|
||||
|
||||
void Network::RemovePlayer(std::unique_ptr<NetworkConnection>& connection)
|
||||
|
@ -2367,9 +2493,9 @@ void Network::Client_Handle_AUTH(NetworkConnection& connection, NetworkPacket& p
|
|||
|
||||
void Network::Server_Client_Joined(const char* name, const std::string& keyhash, NetworkConnection& connection)
|
||||
{
|
||||
NetworkPlayer* player = AddPlayer(name, keyhash);
|
||||
auto player = AddPlayer(name, keyhash);
|
||||
connection.Player = player;
|
||||
if (player)
|
||||
if (player != nullptr)
|
||||
{
|
||||
char text[256];
|
||||
const char* player_name = static_cast<const char*>(player->Name.c_str());
|
||||
|
@ -2387,6 +2513,8 @@ void Network::Server_Client_Joined(const char* name, const std::string& keyhash,
|
|||
player_name = static_cast<const char*>(playerNameHash.c_str());
|
||||
format_string(text, 256, STR_MULTIPLAYER_PLAYER_HAS_JOINED_THE_GAME, &player_name);
|
||||
AppendServerLog(text);
|
||||
|
||||
ProcessPlayerJoinedPluginHooks(player->Id);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2668,9 +2796,16 @@ void Network::Server_Handle_AUTH(NetworkConnection& connection, NetworkPacket& p
|
|||
}
|
||||
else if (connection.AuthStatus == NETWORK_AUTH_VERIFIED)
|
||||
{
|
||||
connection.AuthStatus = NETWORK_AUTH_OK;
|
||||
const std::string hash = connection.Key.PublicKeyHash();
|
||||
Server_Client_Joined(name, hash, connection);
|
||||
if (ProcessPlayerAuthenticatePluginHooks(connection, name, hash))
|
||||
{
|
||||
connection.AuthStatus = NETWORK_AUTH_OK;
|
||||
Server_Client_Joined(name, hash, connection);
|
||||
}
|
||||
else
|
||||
{
|
||||
connection.AuthStatus = NETWORK_AUTH_VERIFICATIONFAILURE;
|
||||
}
|
||||
}
|
||||
else if (connection.AuthStatus != NETWORK_AUTH_REQUIREPASSWORD)
|
||||
{
|
||||
|
@ -2886,7 +3021,7 @@ void Network::Client_Handle_CHAT([[maybe_unused]] NetworkConnection& connection,
|
|||
}
|
||||
}
|
||||
|
||||
static bool ProcessChatMessagePluginHooks(const NetworkPlayer& player, std::string& text)
|
||||
static bool ProcessChatMessagePluginHooks(uint8_t playerId, std::string& text)
|
||||
{
|
||||
# ifdef ENABLE_SCRIPTING
|
||||
auto& hookEngine = GetContext()->GetScriptEngine().GetHookEngine();
|
||||
|
@ -2896,7 +3031,7 @@ static bool ProcessChatMessagePluginHooks(const NetworkPlayer& player, std::stri
|
|||
|
||||
// Create event args object
|
||||
auto objIdx = duk_push_object(ctx);
|
||||
duk_push_number(ctx, static_cast<int32_t>(player.Id));
|
||||
duk_push_number(ctx, playerId);
|
||||
duk_put_prop_string(ctx, objIdx, "player");
|
||||
duk_push_string(ctx, text.c_str());
|
||||
duk_put_prop_string(ctx, objIdx, "message");
|
||||
|
@ -2940,7 +3075,7 @@ void Network::Server_Handle_CHAT(NetworkConnection& connection, NetworkPacket& p
|
|||
std::string text = szText;
|
||||
if (connection.Player != nullptr)
|
||||
{
|
||||
if (!ProcessChatMessagePluginHooks(*connection.Player, text))
|
||||
if (!ProcessChatMessagePluginHooks(connection.Player->Id, text))
|
||||
{
|
||||
// Message not to be relayed
|
||||
return;
|
||||
|
@ -3385,6 +3520,26 @@ money32 network_get_player_money_spent(uint32_t index)
|
|||
return gNetwork.player_list[index]->MoneySpent;
|
||||
}
|
||||
|
||||
std::string network_get_player_ip_address(uint32_t id)
|
||||
{
|
||||
auto conn = gNetwork.GetPlayerConnection(id);
|
||||
if (conn != nullptr && conn->Socket != nullptr)
|
||||
{
|
||||
return conn->Socket->GetIpAddress();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string network_get_player_public_key_hash(uint32_t id)
|
||||
{
|
||||
auto player = gNetwork.GetPlayerByID(id);
|
||||
if (player != nullptr)
|
||||
{
|
||||
return player->KeyHash;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void network_add_player_money_spent(uint32_t index, money32 cost)
|
||||
{
|
||||
gNetwork.player_list[index]->AddMoneySpent(cost);
|
||||
|
@ -3830,7 +3985,7 @@ void network_send_map()
|
|||
gNetwork.Server_Send_MAP();
|
||||
}
|
||||
|
||||
void network_send_chat(const char* text)
|
||||
void network_send_chat(const char* text, const std::vector<uint8_t>& playerIds)
|
||||
{
|
||||
if (gNetwork.GetMode() == NETWORK_MODE_CLIENT)
|
||||
{
|
||||
|
@ -3838,10 +3993,22 @@ void network_send_chat(const char* text)
|
|||
}
|
||||
else if (gNetwork.GetMode() == NETWORK_MODE_SERVER)
|
||||
{
|
||||
NetworkPlayer* player = gNetwork.GetPlayerByID(gNetwork.GetPlayerID());
|
||||
const char* formatted = gNetwork.FormatChat(player, text);
|
||||
chat_history_add(formatted);
|
||||
gNetwork.Server_Send_CHAT(formatted);
|
||||
std::string message = text;
|
||||
if (ProcessChatMessagePluginHooks(gNetwork.GetPlayerID(), message))
|
||||
{
|
||||
auto player = gNetwork.GetPlayerByID(gNetwork.GetPlayerID());
|
||||
if (player != nullptr)
|
||||
{
|
||||
auto formatted = gNetwork.FormatChat(player, message.c_str());
|
||||
if (playerIds.empty()
|
||||
|| std::find(playerIds.begin(), playerIds.end(), gNetwork.GetPlayerID()) != playerIds.end())
|
||||
{
|
||||
// Server is one of the recipients
|
||||
chat_history_add(formatted);
|
||||
}
|
||||
gNetwork.Server_Send_CHAT(formatted, playerIds);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4054,6 +4221,14 @@ money32 network_get_player_money_spent(uint32_t index)
|
|||
{
|
||||
return MONEY(0, 0);
|
||||
}
|
||||
std::string network_get_player_ip_address(uint32_t id)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
std::string network_get_player_public_key_hash(uint32_t id)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
void network_add_player_money_spent(uint32_t index, money32 cost)
|
||||
{
|
||||
}
|
||||
|
@ -4154,7 +4329,7 @@ int32_t network_get_pickup_peep_old_x(uint8_t playerid)
|
|||
{
|
||||
return _pickup_peep_old_x;
|
||||
}
|
||||
void network_send_chat(const char* text)
|
||||
void network_send_chat(const char* text, const std::vector<uint8_t>& playerIds)
|
||||
{
|
||||
}
|
||||
void network_send_password(const std::string& password)
|
||||
|
|
|
@ -201,6 +201,7 @@ private:
|
|||
uint16_t _listeningPort = 0;
|
||||
SOCKET _socket = INVALID_SOCKET;
|
||||
|
||||
std::string _ipAddress;
|
||||
std::string _hostName;
|
||||
std::future<void> _connectFuture;
|
||||
std::string _error;
|
||||
|
@ -322,18 +323,21 @@ public:
|
|||
}
|
||||
else
|
||||
{
|
||||
auto ipAddress = GetIpAddressFromSocket(reinterpret_cast<sockaddr_in*>(&client_addr));
|
||||
|
||||
char hostName[NI_MAXHOST];
|
||||
int32_t rc = getnameinfo(
|
||||
reinterpret_cast<struct sockaddr*>(&client_addr), client_len, hostName, sizeof(hostName), nullptr, 0,
|
||||
NI_NUMERICHOST | NI_NUMERICSERV);
|
||||
SetOption(socket, IPPROTO_TCP, TCP_NODELAY, true);
|
||||
|
||||
if (rc == 0)
|
||||
{
|
||||
tcpSocket = std::unique_ptr<ITcpSocket>(new TcpSocket(socket, hostName));
|
||||
tcpSocket = std::unique_ptr<ITcpSocket>(new TcpSocket(socket, hostName, ipAddress));
|
||||
}
|
||||
else
|
||||
{
|
||||
tcpSocket = std::unique_ptr<ITcpSocket>(new TcpSocket(socket, ""));
|
||||
tcpSocket = std::unique_ptr<ITcpSocket>(new TcpSocket(socket, "", ipAddress));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -546,11 +550,17 @@ public:
|
|||
return _hostName.empty() ? nullptr : _hostName.c_str();
|
||||
}
|
||||
|
||||
std::string GetIpAddress() const override
|
||||
{
|
||||
return _ipAddress;
|
||||
}
|
||||
|
||||
private:
|
||||
explicit TcpSocket(SOCKET socket, const std::string& hostName)
|
||||
explicit TcpSocket(SOCKET socket, const std::string& hostName, const std::string& ipAddress)
|
||||
{
|
||||
_socket = socket;
|
||||
_hostName = hostName;
|
||||
_ipAddress = ipAddress;
|
||||
_status = SOCKET_STATUS_CONNECTED;
|
||||
}
|
||||
|
||||
|
@ -563,6 +573,32 @@ private:
|
|||
}
|
||||
_status = SOCKET_STATUS_CLOSED;
|
||||
}
|
||||
|
||||
std::string GetIpAddressFromSocket(const sockaddr_in* addr)
|
||||
{
|
||||
std::string result;
|
||||
# if defined(__MINGW32__)
|
||||
if (addr->sin_family == AF_INET)
|
||||
{
|
||||
result = inet_ntoa(addr->sin_addr);
|
||||
}
|
||||
# else
|
||||
if (addr->sin_family == AF_INET)
|
||||
{
|
||||
char str[INET_ADDRSTRLEN]{};
|
||||
inet_ntop(AF_INET, &addr->sin_addr, str, sizeof(str));
|
||||
result = str;
|
||||
}
|
||||
else if (addr->sin_family == AF_INET6)
|
||||
{
|
||||
auto addrv6 = reinterpret_cast<const sockaddr_in6*>(&addr);
|
||||
char str[INET6_ADDRSTRLEN]{};
|
||||
inet_ntop(AF_INET6, &addrv6->sin6_addr, str, sizeof(str));
|
||||
result = str;
|
||||
}
|
||||
# endif
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
class UdpSocket final : public IUdpSocket, protected Socket
|
||||
|
|
|
@ -55,6 +55,7 @@ public:
|
|||
virtual SOCKET_STATUS GetStatus() const abstract;
|
||||
virtual const char* GetError() const abstract;
|
||||
virtual const char* GetHostName() const abstract;
|
||||
virtual std::string GetIpAddress() const abstract;
|
||||
|
||||
virtual void Listen(uint16_t port) abstract;
|
||||
virtual void Listen(const std::string& address, uint16_t port) abstract;
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
struct json_t;
|
||||
struct GameAction;
|
||||
|
@ -61,6 +62,8 @@ uint32_t network_get_player_flags(uint32_t index);
|
|||
int32_t network_get_player_ping(uint32_t index);
|
||||
int32_t network_get_player_id(uint32_t index);
|
||||
money32 network_get_player_money_spent(uint32_t index);
|
||||
std::string network_get_player_ip_address(uint32_t id);
|
||||
std::string network_get_player_public_key_hash(uint32_t id);
|
||||
void network_add_player_money_spent(uint32_t index, money32 cost);
|
||||
int32_t network_get_player_last_action(uint32_t index, int32_t time);
|
||||
void network_set_player_last_action(uint32_t index, int32_t command);
|
||||
|
@ -92,7 +95,7 @@ void network_set_pickup_peep_old_x(uint8_t playerid, int32_t x);
|
|||
int32_t network_get_pickup_peep_old_x(uint8_t playerid);
|
||||
|
||||
void network_send_map();
|
||||
void network_send_chat(const char* text);
|
||||
void network_send_chat(const char* text, const std::vector<uint8_t>& playerIds = {});
|
||||
void network_send_game_action(const GameAction* action);
|
||||
void network_enqueue_game_action(const GameAction* action);
|
||||
void network_send_password(const std::string& password);
|
||||
|
|
|
@ -37,6 +37,11 @@ namespace OpenRCT2::Scripting
|
|||
return value.type() == DukValue::NUMBER ? value.as_int() : defaultValue;
|
||||
}
|
||||
|
||||
template<> inline bool AsOrDefault(const DukValue& value, const bool& defaultValue)
|
||||
{
|
||||
return value.type() == DukValue::BOOLEAN ? value.as_bool() : defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows creation of an object on the duktape stack and setting properties on it before
|
||||
* retrieving the DukValue instance of it.
|
||||
|
|
|
@ -25,6 +25,9 @@ HOOK_TYPE OpenRCT2::Scripting::GetHookType(const std::string& name)
|
|||
{ "interval.tick", HOOK_TYPE::INTERVAL_TICK },
|
||||
{ "interval.day", HOOK_TYPE::INTERVAL_DAY },
|
||||
{ "network.chat", HOOK_TYPE::NETWORK_CHAT },
|
||||
{ "network.authenticate", HOOK_TYPE::NETWORK_AUTHENTICATE },
|
||||
{ "network.join", HOOK_TYPE::NETWORK_JOIN },
|
||||
{ "network.leave", HOOK_TYPE::NETWORK_LEAVE },
|
||||
});
|
||||
auto result = LookupTable.find(name);
|
||||
return (result != LookupTable.end()) ? result->second : HOOK_TYPE::UNDEFINED;
|
||||
|
|
|
@ -33,6 +33,9 @@ namespace OpenRCT2::Scripting
|
|||
INTERVAL_TICK,
|
||||
INTERVAL_DAY,
|
||||
NETWORK_CHAT,
|
||||
NETWORK_AUTHENTICATE,
|
||||
NETWORK_JOIN,
|
||||
NETWORK_LEAVE,
|
||||
COUNT,
|
||||
UNDEFINED = -1,
|
||||
};
|
||||
|
|
|
@ -233,6 +233,16 @@ namespace OpenRCT2::Scripting
|
|||
# endif
|
||||
}
|
||||
|
||||
std::string ipAddress_get() const
|
||||
{
|
||||
return network_get_player_ip_address(_id);
|
||||
}
|
||||
|
||||
std::string publicKeyHash_get() const
|
||||
{
|
||||
return network_get_player_public_key_hash(_id);
|
||||
}
|
||||
|
||||
static void Register(duk_context* ctx)
|
||||
{
|
||||
dukglue_register_property(ctx, &ScPlayer::id_get, nullptr, "id");
|
||||
|
@ -241,6 +251,8 @@ namespace OpenRCT2::Scripting
|
|||
dukglue_register_property(ctx, &ScPlayer::ping_get, nullptr, "ping");
|
||||
dukglue_register_property(ctx, &ScPlayer::commandsRan_get, nullptr, "commandsRan");
|
||||
dukglue_register_property(ctx, &ScPlayer::moneySpent_get, nullptr, "moneySpent");
|
||||
dukglue_register_property(ctx, &ScPlayer::ipAddress_get, nullptr, "ipAddress");
|
||||
dukglue_register_property(ctx, &ScPlayer::publicKeyHash_get, nullptr, "publicKeyHash");
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -271,7 +283,7 @@ namespace OpenRCT2::Scripting
|
|||
# endif
|
||||
return "none";
|
||||
}
|
||||
int32_t players_get() const
|
||||
int32_t numPlayers_get() const
|
||||
{
|
||||
# ifndef DISABLE_NETWORK
|
||||
return network_get_num_players();
|
||||
|
@ -279,7 +291,7 @@ namespace OpenRCT2::Scripting
|
|||
return 0;
|
||||
# endif
|
||||
}
|
||||
int32_t groups_get() const
|
||||
int32_t numGroups_get() const
|
||||
{
|
||||
# ifndef DISABLE_NETWORK
|
||||
return network_get_num_groups();
|
||||
|
@ -303,6 +315,34 @@ namespace OpenRCT2::Scripting
|
|||
# endif
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<ScPlayerGroup>> groups_get() const
|
||||
{
|
||||
std::vector<std::shared_ptr<ScPlayerGroup>> groups;
|
||||
# ifndef DISABLE_NETWORK
|
||||
auto numGroups = network_get_num_groups();
|
||||
for (int32_t i = 0; i < numGroups; i++)
|
||||
{
|
||||
auto groupId = network_get_group_id(i);
|
||||
groups.push_back(std::make_shared<ScPlayerGroup>(groupId));
|
||||
}
|
||||
# endif
|
||||
return groups;
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<ScPlayer>> players_get() const
|
||||
{
|
||||
std::vector<std::shared_ptr<ScPlayer>> players;
|
||||
# ifndef DISABLE_NETWORK
|
||||
auto numPlayers = network_get_num_players();
|
||||
for (int32_t i = 0; i < numPlayers; i++)
|
||||
{
|
||||
auto playerId = network_get_player_id(i);
|
||||
players.push_back(std::make_shared<ScPlayer>(playerId));
|
||||
}
|
||||
# endif
|
||||
return players;
|
||||
}
|
||||
|
||||
std::shared_ptr<ScPlayer> getPlayer(int32_t index) const
|
||||
{
|
||||
# ifndef DISABLE_NETWORK
|
||||
|
@ -329,6 +369,27 @@ namespace OpenRCT2::Scripting
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
void addGroup()
|
||||
{
|
||||
# ifndef DISABLE_NETWORK
|
||||
auto networkModifyGroup = NetworkModifyGroupAction(ModifyGroupType::AddGroup);
|
||||
GameActions::Execute(&networkModifyGroup);
|
||||
# endif
|
||||
}
|
||||
|
||||
void removeGroup(int32_t index)
|
||||
{
|
||||
# ifndef DISABLE_NETWORK
|
||||
auto numGroups = network_get_num_groups();
|
||||
if (index < numGroups)
|
||||
{
|
||||
auto groupId = network_get_group_id(index);
|
||||
auto networkAction = NetworkModifyGroupAction(ModifyGroupType::RemoveGroup, groupId);
|
||||
GameActions::Execute(&networkAction);
|
||||
}
|
||||
# endif
|
||||
}
|
||||
|
||||
void kickPlayer(int32_t index)
|
||||
{
|
||||
# ifndef DISABLE_NETWORK
|
||||
|
@ -347,7 +408,26 @@ namespace OpenRCT2::Scripting
|
|||
# ifndef DISABLE_NETWORK
|
||||
if (players.is_array())
|
||||
{
|
||||
duk_error(players.context(), DUK_ERR_ERROR, "Not yet supported");
|
||||
if (network_get_mode() == NETWORK_MODE_SERVER)
|
||||
{
|
||||
std::vector<uint8_t> playerIds;
|
||||
auto playerArray = players.as_array();
|
||||
for (const auto& item : playerArray)
|
||||
{
|
||||
if (item.type() == DukValue::Type::NUMBER)
|
||||
{
|
||||
playerIds.push_back(static_cast<uint8_t>(item.as_int()));
|
||||
}
|
||||
}
|
||||
if (!playerArray.empty())
|
||||
{
|
||||
network_send_chat(message.c_str(), playerIds);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
duk_error(players.context(), DUK_ERR_ERROR, "Only servers can send private messages.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -359,10 +439,14 @@ namespace OpenRCT2::Scripting
|
|||
static void Register(duk_context* ctx)
|
||||
{
|
||||
dukglue_register_property(ctx, &ScNetwork::mode_get, nullptr, "mode");
|
||||
dukglue_register_property(ctx, &ScNetwork::numGroups_get, nullptr, "numGroups");
|
||||
dukglue_register_property(ctx, &ScNetwork::numPlayers_get, nullptr, "numPlayers");
|
||||
dukglue_register_property(ctx, &ScNetwork::groups_get, nullptr, "groups");
|
||||
dukglue_register_property(ctx, &ScNetwork::players_get, nullptr, "players");
|
||||
dukglue_register_property(ctx, &ScNetwork::defaultGroup_get, &ScNetwork::defaultGroup_set, "defaultGroup");
|
||||
dukglue_register_method(ctx, &ScNetwork::addGroup, "addGroup");
|
||||
dukglue_register_method(ctx, &ScNetwork::getGroup, "getGroup");
|
||||
dukglue_register_method(ctx, &ScNetwork::removeGroup, "removeGroup");
|
||||
dukglue_register_method(ctx, &ScNetwork::getPlayer, "getPlayer");
|
||||
dukglue_register_method(ctx, &ScNetwork::kickPlayer, "kickPlayer");
|
||||
dukglue_register_method(ctx, &ScNetwork::sendMessage, "sendMessage");
|
||||
|
|
Loading…
Reference in New Issue