Add ability to toggle individual action permissions

This commit is contained in:
zsilencer 2016-01-21 20:32:51 -07:00
parent 6e3068cd0a
commit 7a249c6457
7 changed files with 678 additions and 81 deletions

View File

@ -3981,6 +3981,69 @@ STR_5639 :{SMALLFONT}{BLACK}Show list of players
STR_5640 :{SMALLFONT}{BLACK}Manage groups
STR_5641 :Default Group:
STR_5642 :Group:
STR_5643 :Add Group
STR_5644 :Remove Group
STR_5645 :Chat
STR_5646 :Set Ride Appearance
STR_5647 :Pause/Unpause
STR_5648 :Place Track
STR_5649 :Remove Track
STR_5650 :Create Ride
STR_5651 :Remove Ride
STR_5652 :Set Ride Status
STR_5653 :Set Ride Vehicles
STR_5654 :Set Ride Name
STR_5655 :Set Ride Setting
STR_5656 :Place Ride Entrance/Exit
STR_5657 :Remove Ride Entrance/Exit
STR_5658 :Remove Scenery
STR_5659 :Place Scenery
STR_5660 :Place Path
STR_5661 :Place Path From Track
STR_5662 :Remove Path
STR_5663 :Change Surface Style
STR_5664 :Set Ride Price
STR_5665 :Set Peep Name
STR_5666 :Raise Land
STR_5667 :Lower Land
STR_5668 :Smooth Land
STR_5669 :Raise Water
STR_5670 :Lower Water
STR_5671 :Set Brakes Speed
STR_5672 :Hire New Staff
STR_5673 :Set Staff Patrol
STR_5674 :Fire Staff
STR_5675 :Set Staff Order
STR_5676 :Set Park Name
STR_5677 :Open/Close Park
STR_5678 :Buy Land Rights
STR_5679 :Place Park Entrance
STR_5680 :Remove Park Entrance
STR_5681 :Set Maze Track
STR_5682 :Set Park Entrance Fee
STR_5683 :Set Staff Colour
STR_5684 :Place Fence
STR_5685 :Remove Fence
STR_5686 :Place Large Scenery
STR_5687 :Set Current Loan
STR_5688 :Set Research Funding
STR_5689 :Place Track Design
STR_5690 :Start Marketing Campaign
STR_5691 :Place Maze Design
STR_5692 :Place Banner
STR_5693 :Remove Banner
STR_5694 :Set Scenery Colour
STR_5695 :Set Fence Colour
STR_5696 :Set Large Scenery Colour
STR_5697 :Set Banner Colour
STR_5698 :Set Land Ownership
STR_5699 :Clear Scenery
STR_5700 :Set Banner Name
STR_5701 :Set Sign Name
STR_5702 :Set Banner Style
STR_5703 :Set Sign Style
STR_5704 :Set Player Group
STR_5705 :Modify Groups
#############
# Scenarios #

View File

@ -418,8 +418,6 @@ static int game_check_affordability(int cost)
return MONEY32_UNDEFINED;
}
static GAME_COMMAND_POINTER* new_game_command_table[63];
/**
*
* rct2: 0x006677F2
@ -1177,7 +1175,7 @@ void game_load_or_quit_no_save_prompt()
}
}
static GAME_COMMAND_POINTER* new_game_command_table[63] = {
GAME_COMMAND_POINTER* new_game_command_table[64] = {
game_command_set_ride_appearance,
game_command_set_land_height,
game_pause_toggle,
@ -1240,5 +1238,6 @@ static GAME_COMMAND_POINTER* new_game_command_table[63] = {
game_command_set_sign_name,
game_command_set_banner_style,
game_command_set_sign_style,
game_command_set_player_group
game_command_set_player_group,
game_command_modify_groups
};

View File

@ -88,7 +88,8 @@ enum GAME_COMMAND {
GAME_COMMAND_SET_SIGN_NAME,
GAME_COMMAND_SET_BANNER_STYLE,
GAME_COMMAND_SET_SIGN_STYLE,
GAME_COMMAND_SET_PLAYER_GROUP
GAME_COMMAND_SET_PLAYER_GROUP,
GAME_COMMAND_MODIFY_GROUPS
};
enum {
@ -113,6 +114,8 @@ extern GAME_COMMAND_CALLBACK_POINTER* game_command_callback;
int game_command_callback_get_index(GAME_COMMAND_CALLBACK_POINTER* callback);
GAME_COMMAND_CALLBACK_POINTER* game_command_callback_get_callback(int index);
extern GAME_COMMAND_POINTER* new_game_command_table[64];
extern int gGameSpeed;
extern float gDayNightCycle;
extern bool gInUpdateCode;

View File

@ -2268,6 +2268,70 @@ enum {
STR_GROUPS_TIP = 5640,
STR_DEFAULT_GROUP = 5641,
STR_GROUP = 5642,
STR_ADD_GROUP = 5643,
STR_REMOVE_GROUP = 5644,
STR_ACTION_CHAT = 5645,
STR_ACTION_SET_RIDE_APPEARANCE = 5646,
STR_ACTION_TOGGLE_PAUSE = 5647,
STR_ACTION_PLACE_TRACK = 5648,
STR_ACTION_REMOVE_TRACK = 5649,
STR_ACTION_CREATE_RIDE = 5650,
STR_ACTION_REMOVE_RIDE = 5651,
STR_ACTION_SET_RIDE_STATUS = 5652,
STR_ACTION_SET_RIDE_VEHICLES = 5653,
STR_ACTION_SET_RIDE_NAME = 5654,
STR_ACTION_SET_RIDE_SETTING = 5655,
STR_ACTION_PLACE_RIDE_ENTRANCE_EXIT = 5656,
STR_ACTION_REMOVE_RIDE_ENTRANCE_EXIT = 5657,
STR_ACTION_REMOVE_SCENERY = 5658,
STR_ACTION_PLACE_SCENERY = 5659,
STR_ACTION_PLACE_PATH = 5660,
STR_ACTION_PLACE_PATH_FROM_TRACK = 5661,
STR_ACTION_REMOVE_PATH = 5662,
STR_ACTION_CHANGE_SURFACE_STYLE = 5663,
STR_ACTION_SET_RIDE_PRICE = 5664,
STR_ACTION_SET_PEEP_NAME = 5665,
STR_ACTION_RAISE_LAND = 5666,
STR_ACTION_LOWER_LAND = 5667,
STR_ACTION_SMOOTH_LAND = 5668,
STR_ACTION_RAISE_WATER = 5669,
STR_ACTION_LOWER_WATER = 5670,
STR_ACTION_SET_BRAKES_SPEED = 5671,
STR_ACTION_HIRE_NEW_STAFF = 5672,
STR_ACTION_SET_STAFF_PATROL = 5673,
STR_ACTION_FIRE_STAFF = 5674,
STR_ACTION_SET_STAFF_ORDER = 5675,
STR_ACTION_SET_PARK_NAME = 5676,
STR_ACTION_OPEN_CLOSE_PARK = 5677,
STR_ACTION_BUY_LAND_RIGHTS = 5678,
STR_ACTION_PLACE_PARK_ENTRANCE = 5679,
STR_ACTION_REMOVE_PARK_ENTRANCE = 5680,
STR_ACTION_SET_MAZE_TRACK = 5681,
STR_ACTION_SET_PARK_ENTRANCE_FEE = 5682,
STR_ACTION_SET_STAFF_COLOUR = 5683,
STR_ACTION_PLACE_FENCE = 5684,
STR_ACTION_REMOVE_FENCE = 5685,
STR_ACTION_PLACE_LARGE_SCENERY = 5686,
STR_ACTION_SET_CURRENT_LOAN = 5687,
STR_ACTION_SET_RESEARCH_FUNDING = 5688,
STR_ACTION_PLACE_TRACK_DESIGN = 5689,
STR_ACTION_START_MARKETING_CAMPAIGN = 5690,
STR_ACTION_PLACE_MAZE_DESIGN = 5691,
STR_ACTION_PLACE_BANNER = 5692,
STR_ACTION_REMOVE_BANNER = 5693,
STR_ACTION_SET_SCENERY_COLOUR = 5694,
STR_ACTION_SET_FENCE_COLOUR = 5695,
STR_ACTION_SET_LARGE_SCENERY_COLOUR = 5696,
STR_ACTION_SET_BANNER_COLOUR = 5697,
STR_ACTION_SET_LAND_OWNERSHIP = 5698,
STR_ACTION_CLEAR_SCENERY = 5699,
STR_ACTION_SET_BANNER_NAME = 5700,
STR_ACTION_SET_SIGN_NAME = 5701,
STR_ACTION_SET_BANNER_STYLE = 5702,
STR_ACTION_SET_SIGN_STYLE = 5703,
STR_ACTION_SET_PLAYER_GROUP = 5704,
STR_ACTION_MODIFY_GROUPS = 5705,
// Have to include resource strings (from scenarios and objects) for the time being now that language is partially working
STR_COUNT = 32768

View File

@ -53,6 +53,7 @@ extern "C" {
#pragma comment(lib, "Ws2_32.lib")
Network gNetwork;
NetworkActions gNetworkActions;
enum {
NETWORK_READPACKET_SUCCESS,
@ -73,6 +74,7 @@ enum {
NETWORK_COMMAND_SETDISCONNECTMSG,
NETWORK_COMMAND_GAMEINFO,
NETWORK_COMMAND_SHOWERROR,
NETWORK_COMMAND_GROUPLIST,
NETWORK_COMMAND_MAX,
NETWORK_COMMAND_INVALID = -1
};
@ -180,15 +182,34 @@ bool NetworkPacket::CommandRequiresAuth()
}
}
NetworkPlayer::NetworkPlayer(const char* name)
NetworkPlayer::NetworkPlayer()
{
safe_strcpy((char*)NetworkPlayer::name, name, sizeof(NetworkPlayer::name));
NetworkPlayer::name[sizeof(NetworkPlayer::name) - 1] = 0;
name[0] = 0;
ping = 0;
flags = 0;
money_spent = MONEY(0, 0);
commands_ran = 0;
group = 0;
reserved = 0;
}
void NetworkPlayer::Read(NetworkPacket& packet)
{
const char* name = packet.ReadString();
SetName(name);
packet >> id >> flags >> group >> reserved;
}
void NetworkPlayer::Write(NetworkPacket& packet)
{
packet.WriteString((const char*)name);
packet << id << flags << group << reserved;
}
void NetworkPlayer::SetName(const char* name)
{
safe_strcpy((char*)NetworkPlayer::name, name, sizeof(NetworkPlayer::name));
NetworkPlayer::name[sizeof(NetworkPlayer::name) - 1] = 0;
}
void NetworkPlayer::AddMoneySpent(money32 cost)
@ -198,25 +219,102 @@ void NetworkPlayer::AddMoneySpent(money32 cost)
window_invalidate_by_number(WC_PLAYER, id);
}
int NetworkActions::FindGameCommand(int command)
{
auto it = std::find_if(actions.begin(), actions.end(), [&command](NetworkAction const& action) { return action.game_command == command; });
if (it != actions.end()) {
return it - actions.begin();
}
return -1;
}
NetworkGroup::NetworkGroup()
{
permissions = 0;
name_string_id = STR_NONE;
}
NetworkGroup::~NetworkGroup()
{
user_string_free(name_string_id);
FreeNameStringId();
}
bool NetworkGroup::CanRun(int command)
void NetworkGroup::Read(NetworkPacket& packet)
{
if (permissions) {
return true;
packet >> id;
SetName(packet.ReadString());
for (size_t i = 0; i < actions_allowed.size(); i++) {
packet >> actions_allowed[i];
}
}
void NetworkGroup::Write(NetworkPacket& packet)
{
packet << id;
packet.WriteString(GetName().c_str());
for (size_t i = 0; i < actions_allowed.size(); i++) {
packet << actions_allowed[i];
}
}
void NetworkGroup::FreeNameStringId()
{
if (name_string_id != STR_NONE) {
user_string_free(name_string_id);
name_string_id = STR_NONE;
}
}
void NetworkGroup::ToggleActionPermission(size_t index)
{
size_t byte = index / 8;
size_t bit = index % 8;
if (byte >= actions_allowed.size()) {
return;
}
actions_allowed[byte] ^= (1 << bit);
}
bool NetworkGroup::CanPerformAction(size_t index)
{
size_t byte = index / 8;
size_t bit = index % 8;
if (byte >= actions_allowed.size()) {
return false;
}
return (actions_allowed[byte] & (1 << bit)) ? true : false;
}
bool NetworkGroup::CanPerformGameCommand(uint32 command)
{
if ((size_t)command >= Util::CountOf(new_game_command_table)) {
return false;
}
int action = gNetworkActions.FindGameCommand(command);
if (action != -1) {
return CanPerformAction(action);
}
return false;
}
std::string& NetworkGroup::GetName()
{
return name;
}
void NetworkGroup::SetName(std::string name)
{
FreeNameStringId();
NetworkGroup::name = name;
}
rct_string_id NetworkGroup::GetNameStringId()
{
if (name_string_id == STR_NONE) {
name_string_id = user_string_allocate(128, name.c_str());
}
return name_string_id;
}
NetworkConnection::NetworkConnection()
{
authstatus = NETWORK_AUTH_NONE;
@ -462,17 +560,17 @@ bool Network::Init()
#endif
// Hardcoded permission groups
std::unique_ptr<NetworkGroup> admin(new NetworkGroup()); // change to make_unique in c++14
admin->SetName("Admin");
admin->actions_allowed.fill(0xFF);
admin->id = 0;
group_list.push_back(std::move(admin));
std::unique_ptr<NetworkGroup> spectator(new NetworkGroup()); // change to make_unique in c++14
spectator->name = "Spectator";
spectator->permissions = 0;
spectator->id = 0;
spectator->SetName("Spectator");
spectator->ToggleActionPermission(0);
spectator->id = 1;
group_list.push_back(std::move(spectator));
std::unique_ptr<NetworkGroup> fullaccess(new NetworkGroup()); // change to make_unique in c++14
fullaccess->name = "Full Access";
fullaccess->permissions = 1;
fullaccess->id = 1;
group_list.push_back(std::move(fullaccess));
SetDefaultGroup(0);
SetDefaultGroup(1);
status = NETWORK_STATUS_READY;
return true;
@ -573,9 +671,10 @@ bool Network::BeginServer(unsigned short port, const char* address)
return false;
}
NetworkPlayer* player = AddPlayer(gConfigNetwork.player_name);
NetworkPlayer* player = AddPlayer();
player->SetName(gConfigNetwork.player_name);
player->flags |= NETWORK_PLAYER_FLAG_ISSERVER;
player->group = 1;
player->group = 0;
player_id = player->id;
printf("Ready for clients...\n");
@ -1022,6 +1121,30 @@ void Network::AdvertiseHeartbeat()
#endif
}
NetworkGroup* Network::AddGroup()
{
NetworkGroup* addedgroup = nullptr;
int newid = -1;
// Find first unused group id
for (int id = 0; id < 255; id++) {
if (std::find_if(group_list.begin(), group_list.end(), [&id](std::unique_ptr<NetworkGroup> const& group) { return group->id == id; }) == group_list.end()) {
newid = id;
break;
}
}
if (newid != -1) {
std::unique_ptr<NetworkGroup> group(new NetworkGroup); // change to make_unique in c++14
group->id = newid;
group->SetName("Group #" + std::to_string(newid));
addedgroup = group.get();
group_list.push_back(std::move(group));
if (GetMode() == NETWORK_MODE_SERVER) {
Server_Send_GROUPLIST();
}
}
return addedgroup;
}
uint8 Network::GetDefaultGroup()
{
return default_group;
@ -1073,14 +1196,14 @@ void Network::Server_Send_MAP(NetworkConnection* connection)
size_t chunksize = 1000;
size_t out_size;
unsigned char *compressed = util_zlib_deflate(&buffer[0], size, &out_size);
unsigned char *header = (unsigned char *)strdup("open2_sv6_zlib");
unsigned char *header = (unsigned char *)_strdup("open2_sv6_zlib");
size_t header_len = strlen((char *)header) + 1; // account for null terminator
header = (unsigned char *)realloc(header, header_len + out_size);
memcpy(&header[header_len], compressed, out_size);
out_size += header_len;
free(compressed);
log_verbose("Sending map of size %u bytes, compressed to %u bytes", size, out_size);
for (int i = 0; i < out_size; i += chunksize) {
for (size_t i = 0; i < out_size; i += chunksize) {
int datasize = (std::min)(chunksize, out_size - i);
std::unique_ptr<NetworkPacket> packet = std::move(NetworkPacket::Allocate());
*packet << (uint32)NETWORK_COMMAND_MAP << (uint32)out_size << (uint32)i;
@ -1138,8 +1261,7 @@ void Network::Server_Send_PLAYERLIST()
std::unique_ptr<NetworkPacket> packet = std::move(NetworkPacket::Allocate());
*packet << (uint32)NETWORK_COMMAND_PLAYERLIST << (uint32)player_list.size();
for (unsigned int i = 0; i < player_list.size(); i++) {
packet->WriteString((const char*)player_list[i]->name);
*packet << player_list[i]->id << player_list[i]->flags << player_list[i]->group << player_list[i]->reserved;
player_list[i]->Write(*packet);
}
SendPacketToClients(*packet);
}
@ -1214,6 +1336,13 @@ void Network::Server_Send_SHOWERROR(NetworkConnection& connection, rct_string_id
connection.QueuePacket(std::move(packet));
}
void Network::Server_Send_GROUPLIST()
{
std::unique_ptr<NetworkPacket> packet = std::move(NetworkPacket::Allocate());
*packet << (uint32)NETWORK_COMMAND_GROUPLIST;
SendPacketToClients(*packet);
}
bool Network::ProcessConnection(NetworkConnection& connection)
{
int packetStatus;
@ -1321,7 +1450,7 @@ void Network::RemoveClient(std::unique_ptr<NetworkConnection>& connection)
Server_Send_PLAYERLIST();
}
NetworkPlayer* Network::AddPlayer(const char* name)
NetworkPlayer* Network::AddPlayer()
{
NetworkPlayer* addedplayer = nullptr;
int newid = -1;
@ -1337,14 +1466,11 @@ NetworkPlayer* Network::AddPlayer(const char* name)
newid = 0;
}
if (newid != -1) {
std::unique_ptr<NetworkPlayer> player(new NetworkPlayer(name)); // change to make_unique in c++14
std::unique_ptr<NetworkPlayer> player(new NetworkPlayer); // change to make_unique in c++14
player->id = newid;
player->group = GetDefaultGroup();
addedplayer = player.get();
player_list.push_back(std::move(player));
if (GetMode() == NETWORK_MODE_SERVER) {
Server_Send_PLAYERLIST();
}
}
return addedplayer;
}
@ -1412,17 +1538,19 @@ int Network::Server_Handle_AUTH(NetworkConnection& connection, NetworkPacket& pa
connection.authstatus = NETWORK_AUTH_FULL;
} else {
connection.authstatus = NETWORK_AUTH_OK;
NetworkPlayer* player = AddPlayer(name);
NetworkPlayer* player = AddPlayer();
connection.player = player;
if (player) {
player->SetName(name);
char text[256];
char* lineCh = text;
lineCh = utf8_write_codepoint(lineCh, FORMAT_OUTLINE);
lineCh = utf8_write_codepoint(lineCh, FORMAT_GREEN);
sprintf(lineCh, "%s has joined the game", player->name);
chat_history_add(text);
gNetwork.Server_Send_CHAT(text);
Server_Send_MAP(&connection);
gNetwork.Server_Send_CHAT(text);
Server_Send_PLAYERLIST();
}
}
Server_Send_AUTH(connection);
@ -1541,7 +1669,7 @@ int Network::Server_Handle_GAMECMD(NetworkConnection& connection, NetworkPacket&
// Check if player's group permission allows command to run
NetworkGroup* group = GetGroupByID(connection.player->group);
if (group && !group->CanRun(commandCommand)) {
if (group && !group->CanPerformGameCommand(commandCommand)) {
Server_Send_SHOWERROR(connection, STR_CANT_DO_THIS, STR_PERMISSION_DENIED);
return 0;
}
@ -1582,20 +1710,13 @@ int Network::Client_Handle_PLAYERLIST(NetworkConnection& connection, NetworkPack
packet >> size;
std::vector<uint8> ids;
for (unsigned int i = 0; i < size; i++) {
const char* name = packet.ReadString();
uint8 id;
uint8 flags;
uint8 group;
uint16 reserved;
packet >> id >> flags >> group >> reserved;
ids.push_back(id);
if (!GetPlayerByID(id)) {
NetworkPlayer* player = AddPlayer(name);
NetworkPlayer tempplayer;
tempplayer.Read(packet);
ids.push_back(tempplayer.id);
if (!GetPlayerByID(tempplayer.id)) {
NetworkPlayer* player = AddPlayer();
if (player) {
player->id = id;
player->flags = flags;
player->group = group;
player->reserved = reserved;
*player = tempplayer;
if (player->flags & NETWORK_PLAYER_FLAG_ISSERVER) {
server_connection.player = player;
}
@ -1810,15 +1931,12 @@ int network_get_num_groups()
const char* network_get_group_name(unsigned int index)
{
return gNetwork.group_list[index]->name.c_str();
return gNetwork.group_list[index]->GetName().c_str();
}
rct_string_id network_get_group_name_string_id(unsigned int index)
{
if (gNetwork.group_list[index]->name_string_id == STR_NONE) {
gNetwork.group_list[index]->name_string_id = user_string_allocate(128, gNetwork.group_list[index]->name.c_str());
}
return gNetwork.group_list[index]->name_string_id;
return gNetwork.group_list[index]->GetNameStringId();
}
void game_command_set_player_group(int* eax, int* ebx, int* ecx, int* edx, int* esi, int* edi, int* ebp)
@ -1846,6 +1964,90 @@ void game_command_set_player_group(int* eax, int* ebx, int* ecx, int* edx, int*
*ebx = 0;
}
void game_command_modify_groups(int *eax, int *ebx, int *ecx, int *edx, int *esi, int *edi, int *ebp)
{
uint8 action = (uint8)*eax;
uint8 groupid = (uint8)(*eax >> 8);
uint8 nameChunkIndex = (uint8)(*eax >> 16);
rct_string_id newUserStringId;
char oldName[128];
static char newName[128];
switch (action)
{
case 0:{ // add group
NetworkGroup* newgroup = gNetwork.AddGroup();
if (!newgroup) {
*ebx = MONEY32_UNDEFINED;
return;
}
}break;
case 1:{ // remove group
}break;
case 2:{ // toggle permission
if (groupid == 0) { // cant change admin group permissions
RCT2_GLOBAL(RCT2_ADDRESS_GAME_COMMAND_ERROR_TITLE, uint16) = STR_CANT_DO_THIS;
*ebx = MONEY32_UNDEFINED;
return;
}
if (*ebx & GAME_COMMAND_FLAG_APPLY) {
int index = *ecx;
NetworkGroup* group = gNetwork.GetGroupByID(groupid);
if (group) {
group->ToggleActionPermission(index);
}
}
}break;
case 3:{ // set group name
size_t nameChunkOffset = nameChunkIndex - 1;
if (nameChunkOffset < 0)
nameChunkOffset = 2;
nameChunkOffset *= 12;
nameChunkOffset = min(nameChunkOffset, Util::CountOf(newName) - 12);
RCT2_GLOBAL(newName + nameChunkOffset + 0, uint32) = *edx;
RCT2_GLOBAL(newName + nameChunkOffset + 4, uint32) = *ebp;
RCT2_GLOBAL(newName + nameChunkOffset + 8, uint32) = *edi;
if (nameChunkIndex != 0) {
*ebx = 0;
return;
}
if (strcmp(oldName, newName) == 0) {
*ebx = 0;
return;
}
if (newName[0] == 0) {
RCT2_GLOBAL(RCT2_ADDRESS_GAME_COMMAND_ERROR_TEXT, uint16) = STR_INVALID_RIDE_ATTRACTION_NAME;
*ebx = MONEY32_UNDEFINED;
return;
}
newUserStringId = user_string_allocate(4, newName);
if (newUserStringId == 0) {
RCT2_GLOBAL(RCT2_ADDRESS_GAME_COMMAND_ERROR_TEXT, uint16) = STR_INVALID_NAME_FOR_PARK;
*ebx = MONEY32_UNDEFINED;
return;
}
if (*ebx & GAME_COMMAND_FLAG_APPLY) {
// Free the old ride name
user_string_free(RCT2_GLOBAL(RCT2_ADDRESS_PARK_NAME, rct_string_id));
RCT2_GLOBAL(RCT2_ADDRESS_PARK_NAME, rct_string_id) = newUserStringId;
gfx_invalidate_screen();
} else {
user_string_free(newUserStringId);
}
}break;
}
*ebx = 0;
}
uint8 network_get_default_group()
{
return gNetwork.GetDefaultGroup();
@ -1856,6 +2058,21 @@ void network_set_default_group(uint8 id)
gNetwork.SetDefaultGroup(id);
}
int network_get_num_actions()
{
return gNetworkActions.actions.size();
}
rct_string_id network_get_action_name_string_id(unsigned int index)
{
return gNetworkActions.actions[index].name;
}
int network_can_perform_action(unsigned int groupindex, unsigned int index)
{
return gNetwork.group_list[groupindex]->CanPerformAction(index);
}
void network_send_map()
{
gNetwork.Server_Send_MAP();
@ -1930,6 +2147,9 @@ rct_string_id network_get_group_name_string_id(unsigned int index) { return -1;
void game_command_set_player_group(int* eax, int* ebx, int* ecx, int* edx, int* esi, int* edi, int* ebp) { }
uint8 network_get_default_group() { return 0; }
void network_set_default_group(uint8 id) { }
int network_get_num_actions() { return 0; }
rct_string_id network_get_action_name_string_id(unsigned int index) { return -1; }
int network_can_perform_action(unsigned int groupindex, unsigned int index) { return 0; }
void network_send_chat(const char* text) {}
void network_send_password(const char* password) {}
void network_close() {}

View File

@ -56,7 +56,9 @@ enum {
extern "C" {
#endif // __cplusplus
#include "../common.h"
#include "../game.h"
#include "../platform/platform.h"
#include "../localisation/string_ids.h"
#ifdef __cplusplus
}
#endif // __cplusplus
@ -98,6 +100,7 @@ extern "C" {
#ifdef __cplusplus
#include <array>
#include <list>
#include <set>
#include <memory>
@ -144,7 +147,10 @@ public:
class NetworkPlayer
{
public:
NetworkPlayer(const char* name);
NetworkPlayer();
void Read(NetworkPacket& packet);
void Write(NetworkPacket& packet);
void SetName(const char* name);
void AddMoneySpent(money32 cost);
uint8 id;
uint8 name[32 + 1];
@ -156,15 +162,104 @@ public:
unsigned int commands_ran;
};
class NetworkAction
{
public:
rct_string_id name;
int game_command;
};
class NetworkActions
{
public:
int FindGameCommand(int command);
const std::vector<NetworkAction> actions = {
{STR_ACTION_CHAT, -1},
{STR_ACTION_SET_RIDE_APPEARANCE, GAME_COMMAND_SET_RIDE_APPEARANCE},
//{STR_NONE, GAME_COMMAND_SET_LAND_HEIGHT},
{STR_ACTION_TOGGLE_PAUSE, GAME_COMMAND_TOGGLE_PAUSE},
{STR_ACTION_PLACE_TRACK, GAME_COMMAND_PLACE_TRACK},
{STR_ACTION_REMOVE_TRACK, GAME_COMMAND_REMOVE_TRACK},
//{STR_NONE, GAME_COMMAND_LOAD_OR_QUIT},
{STR_ACTION_CREATE_RIDE, GAME_COMMAND_CREATE_RIDE},
{STR_ACTION_REMOVE_RIDE, GAME_COMMAND_DEMOLISH_RIDE},
{STR_ACTION_SET_RIDE_STATUS, GAME_COMMAND_SET_RIDE_STATUS},
{STR_ACTION_SET_RIDE_VEHICLES, GAME_COMMAND_SET_RIDE_VEHICLES},
{STR_ACTION_SET_RIDE_NAME, GAME_COMMAND_SET_RIDE_NAME},
{STR_ACTION_SET_RIDE_SETTING, GAME_COMMAND_SET_RIDE_SETTING},
{STR_ACTION_PLACE_RIDE_ENTRANCE_EXIT, GAME_COMMAND_PLACE_RIDE_ENTRANCE_OR_EXIT},
{STR_ACTION_REMOVE_RIDE_ENTRANCE_EXIT, GAME_COMMAND_REMOVE_RIDE_ENTRANCE_OR_EXIT},
{STR_ACTION_REMOVE_SCENERY, GAME_COMMAND_REMOVE_SCENERY},
{STR_ACTION_PLACE_SCENERY, GAME_COMMAND_PLACE_SCENERY},
//{STR_NONE, GAME_COMMAND_SET_WATER_HEIGHT},
{STR_ACTION_PLACE_PATH, GAME_COMMAND_PLACE_PATH},
{STR_ACTION_PLACE_PATH_FROM_TRACK, GAME_COMMAND_PLACE_PATH_FROM_TRACK},
{STR_ACTION_REMOVE_PATH, GAME_COMMAND_REMOVE_PATH},
{STR_ACTION_CHANGE_SURFACE_STYLE, GAME_COMMAND_CHANGE_SURFACE_STYLE},
{STR_ACTION_SET_RIDE_PRICE, GAME_COMMAND_SET_RIDE_PRICE},
{STR_ACTION_SET_PEEP_NAME, GAME_COMMAND_SET_PEEP_NAME},
{STR_ACTION_RAISE_LAND, GAME_COMMAND_RAISE_LAND},
{STR_ACTION_LOWER_LAND, GAME_COMMAND_LOWER_LAND},
{STR_ACTION_SMOOTH_LAND, GAME_COMMAND_EDIT_LAND_SMOOTH},
{STR_ACTION_RAISE_WATER, GAME_COMMAND_RAISE_WATER},
{STR_ACTION_LOWER_WATER, GAME_COMMAND_LOWER_WATER},
{STR_ACTION_SET_BRAKES_SPEED, GAME_COMMAND_SET_BRAKES_SPEED},
{STR_ACTION_HIRE_NEW_STAFF, GAME_COMMAND_HIRE_NEW_STAFF_MEMBER},
{STR_ACTION_SET_STAFF_PATROL, GAME_COMMAND_SET_STAFF_PATROL},
{STR_ACTION_FIRE_STAFF, GAME_COMMAND_FIRE_STAFF_MEMBER},
{STR_ACTION_SET_STAFF_ORDER, GAME_COMMAND_SET_STAFF_ORDER},
{STR_ACTION_SET_PARK_NAME, GAME_COMMAND_SET_PARK_NAME},
{STR_ACTION_OPEN_CLOSE_PARK, GAME_COMMAND_SET_PARK_OPEN},
{STR_ACTION_BUY_LAND_RIGHTS, GAME_COMMAND_BUY_LAND_RIGHTS},
{STR_ACTION_PLACE_PARK_ENTRANCE, GAME_COMMAND_PLACE_PARK_ENTRANCE},
{STR_ACTION_REMOVE_PARK_ENTRANCE, GAME_COMMAND_REMOVE_PARK_ENTRANCE},
{STR_ACTION_SET_MAZE_TRACK, GAME_COMMAND_SET_MAZE_TRACK},
{STR_ACTION_SET_PARK_ENTRANCE_FEE, GAME_COMMAND_SET_PARK_ENTRANCE_FEE},
{STR_ACTION_SET_STAFF_COLOUR, GAME_COMMAND_SET_STAFF_COLOUR},
{STR_ACTION_PLACE_FENCE, GAME_COMMAND_PLACE_FENCE},
{STR_ACTION_REMOVE_FENCE, GAME_COMMAND_REMOVE_FENCE},
{STR_ACTION_PLACE_LARGE_SCENERY, GAME_COMMAND_PLACE_LARGE_SCENERY},
{STR_ACTION_SET_CURRENT_LOAN, GAME_COMMAND_SET_CURRENT_LOAN},
{STR_ACTION_SET_RESEARCH_FUNDING, GAME_COMMAND_SET_RESEARCH_FUNDING},
{STR_ACTION_PLACE_TRACK_DESIGN, GAME_COMMAND_PLACE_TRACK_DESIGN},
{STR_ACTION_START_MARKETING_CAMPAIGN, GAME_COMMAND_START_MARKETING_CAMPAIGN},
{STR_ACTION_PLACE_MAZE_DESIGN, GAME_COMMAND_PLACE_MAZE_DESIGN},
{STR_ACTION_PLACE_BANNER, GAME_COMMAND_PLACE_BANNER},
{STR_ACTION_REMOVE_BANNER, GAME_COMMAND_REMOVE_BANNER},
{STR_ACTION_SET_SCENERY_COLOUR, GAME_COMMAND_SET_SCENERY_COLOUR},
{STR_ACTION_SET_FENCE_COLOUR, GAME_COMMAND_SET_FENCE_COLOUR},
{STR_ACTION_SET_LARGE_SCENERY_COLOUR, GAME_COMMAND_SET_LARGE_SCENERY_COLOUR},
{STR_ACTION_SET_BANNER_COLOUR, GAME_COMMAND_SET_BANNER_COLOUR},
{STR_ACTION_SET_LAND_OWNERSHIP, GAME_COMMAND_SET_LAND_OWNERSHIP},
{STR_ACTION_CLEAR_SCENERY, GAME_COMMAND_CLEAR_SCENERY},
{STR_ACTION_SET_BANNER_NAME, GAME_COMMAND_SET_BANNER_NAME},
{STR_ACTION_SET_SIGN_NAME, GAME_COMMAND_SET_SIGN_NAME},
{STR_ACTION_SET_BANNER_STYLE, GAME_COMMAND_SET_BANNER_STYLE},
{STR_ACTION_SET_SIGN_STYLE, GAME_COMMAND_SET_SIGN_STYLE},
{STR_ACTION_SET_PLAYER_GROUP, GAME_COMMAND_SET_PLAYER_GROUP},
{STR_ACTION_MODIFY_GROUPS, GAME_COMMAND_MODIFY_GROUPS}
};
};
class NetworkGroup
{
public:
NetworkGroup();
~NetworkGroup();
bool CanRun(int command);
std::string name;
uint32 permissions;
void Read(NetworkPacket& packet);
void Write(NetworkPacket& packet);
void FreeNameStringId();
void ToggleActionPermission(size_t index);
bool CanPerformAction(size_t index);
bool CanPerformGameCommand(uint32 command);
std::string& GetName();
void SetName(std::string name);
rct_string_id GetNameStringId();
std::array<uint8, 16> actions_allowed = {0};
uint8 id;
private:
std::string name;
rct_string_id name_string_id;
};
@ -249,6 +344,7 @@ public:
void ShutdownClient();
void AdvertiseRegister();
void AdvertiseHeartbeat();
NetworkGroup* AddGroup();
uint8 GetDefaultGroup();
void SetDefaultGroup(uint8 id);
@ -267,6 +363,7 @@ public:
void Server_Send_SETDISCONNECTMSG(NetworkConnection& connection, const char* msg);
void Server_Send_GAMEINFO(NetworkConnection& connection);
void Server_Send_SHOWERROR(NetworkConnection& connection, rct_string_id title, rct_string_id message);
void Server_Send_GROUPLIST();
std::vector<std::unique_ptr<NetworkPlayer>> player_list;
std::vector<std::unique_ptr<NetworkGroup>> group_list;
@ -277,7 +374,7 @@ private:
void ProcessGameCommandQueue();
void AddClient(SOCKET socket);
void RemoveClient(std::unique_ptr<NetworkConnection>& connection);
NetworkPlayer* AddPlayer(const char* name);
NetworkPlayer* AddPlayer();
void PrintError();
const char* GetMasterServerUrl();
std::string GenerateAdvertiseKey();
@ -378,8 +475,12 @@ int network_get_num_groups();
const char* network_get_group_name(unsigned int index);
rct_string_id network_get_group_name_string_id(unsigned int index);
void game_command_set_player_group(int* eax, int* ebx, int* ecx, int* edx, int* esi, int* edi, int* ebp);
void game_command_modify_groups(int *eax, int *ebx, int *ecx, int *edx, int *esi, int *edi, int *ebp);
uint8 network_get_default_group();
void network_set_default_group(uint8 id);
int network_get_num_actions();
rct_string_id network_get_action_name_string_id(unsigned int index);
int network_can_perform_action(unsigned int groupindex, unsigned int index);
void network_send_map();
void network_send_chat(const char* text);

View File

@ -44,6 +44,11 @@ enum WINDOW_MULTIPLAYER_WIDGET_IDX {
WIDX_DEFAULT_GROUP = 6,
WIDX_DEFAULT_GROUP_DROPDOWN,
WIDX_ADD_GROUP,
WIDX_REMOVE_GROUP,
WIDX_SELECTED_GROUP,
WIDX_SELECTED_GROUP_DROPDOWN,
WIDX_PERMISSIONS_LIST,
};
static rct_widget window_multiplayer_players_widgets[] = {
@ -66,6 +71,11 @@ static rct_widget window_multiplayer_groups_widgets[] = {
{ WWT_TAB, 1, 3, 33, 17, 43, 0x02000144E, STR_GROUPS_TIP }, // tab
{ WWT_DROPDOWN, 1, 141, 315, 46, 57, 0x0FFFFFFFF, STR_NONE }, // default group
{ WWT_DROPDOWN_BUTTON, 1, 305, 315, 47, 56, 876, STR_NONE }, //
{ WWT_DROPDOWN_BUTTON, 1, 44, 134, 65, 76, STR_ADD_GROUP, STR_NONE }, // add group button
{ WWT_DROPDOWN_BUTTON, 1, 178, 268, 65, 76, STR_REMOVE_GROUP, STR_NONE }, // remove group button
{ WWT_DROPDOWN, 1, 72, 246, 80, 91, 0x0FFFFFFFF, STR_NONE }, // selected group
{ WWT_DROPDOWN_BUTTON, 1, 236, 246, 81, 90, 876, STR_NONE }, //
{ WWT_SCROLL, 1, 3, 316, 94, 300, 2, STR_NONE }, // permissions list
{ WIDGETS_END }
};
@ -76,9 +86,11 @@ static rct_widget *window_multiplayer_page_widgets[] = {
const uint64 window_multiplayer_page_enabled_widgets[] = {
(1 << WIDX_CLOSE) | (1 << WIDX_TAB1) | (1 << WIDX_TAB2),
(1 << WIDX_CLOSE) | (1 << WIDX_TAB1) | (1 << WIDX_TAB2) | (1 << WIDX_DEFAULT_GROUP) | (1 << WIDX_DEFAULT_GROUP_DROPDOWN)
(1 << WIDX_CLOSE) | (1 << WIDX_TAB1) | (1 << WIDX_TAB2) | (1 << WIDX_DEFAULT_GROUP) | (1 << WIDX_DEFAULT_GROUP_DROPDOWN) | (1 << WIDX_ADD_GROUP) | (1 << WIDX_REMOVE_GROUP) | (1 << WIDX_SELECTED_GROUP) | (1 << WIDX_SELECTED_GROUP_DROPDOWN)
};
static uint8 _selectedGroup = 0;
static void window_multiplayer_players_mouseup(rct_window *w, int widgetIndex);
static void window_multiplayer_players_resize(rct_window *w);
static void window_multiplayer_players_mousedown(int widgetIndex, rct_window* w, rct_widget* widget);
@ -95,8 +107,12 @@ static void window_multiplayer_groups_resize(rct_window *w);
static void window_multiplayer_groups_mousedown(int widgetIndex, rct_window* w, rct_widget* widget);
static void window_multiplayer_groups_dropdown(rct_window *w, int widgetIndex, int dropdownIndex);
static void window_multiplayer_groups_update(rct_window *w);
static void window_multiplayer_groups_scrollgetsize(rct_window *w, int scrollIndex, int *width, int *height);
static void window_multiplayer_groups_scrollmousedown(rct_window *w, int scrollIndex, int x, int y);
static void window_multiplayer_groups_scrollmouseover(rct_window *w, int scrollIndex, int x, int y);
static void window_multiplayer_groups_invalidate(rct_window *w);
static void window_multiplayer_groups_paint(rct_window *w, rct_drawpixelinfo *dpi);
static void window_multiplayer_groups_scrollpaint(rct_window *w, rct_drawpixelinfo *dpi, int scrollIndex);
static rct_window_event_list window_multiplayer_players_events = {
NULL,
@ -145,10 +161,10 @@ static rct_window_event_list window_multiplayer_groups_events = {
NULL,
NULL,
NULL,
window_multiplayer_groups_scrollgetsize,
window_multiplayer_groups_scrollmousedown,
NULL,
NULL,
NULL,
NULL,
window_multiplayer_groups_scrollmouseover,
NULL,
NULL,
NULL,
@ -157,7 +173,7 @@ static rct_window_event_list window_multiplayer_groups_events = {
NULL,
window_multiplayer_groups_invalidate,
window_multiplayer_groups_paint,
NULL,
window_multiplayer_groups_scrollpaint
};
static rct_window_event_list *window_multiplayer_page_events[] = {
@ -168,7 +184,6 @@ static rct_window_event_list *window_multiplayer_page_events[] = {
const int window_multiplayer_animation_divisor[] = { 4, 4 };
const int window_multiplayer_animation_frames[] = { 8, 16 };
static void window_multiplayer_players_refresh_list(rct_window *w);
static void window_multiplayer_draw_tab_images(rct_window *w, rct_drawpixelinfo *dpi);
static void window_multiplayer_set_page(rct_window* w, int page);
@ -228,7 +243,7 @@ static void window_multiplayer_set_pressed_tab(rct_window *w)
w->pressed_widgets |= 1LL << (WIDX_TAB1 + w->page);
}
static void window_multiplayer_groups_show_defaultgroup_dropdown(rct_window *w, rct_widget *widget)
static void window_multiplayer_groups_show_group_dropdown(rct_window *w, rct_widget *widget)
{
rct_widget *dropdownWidget;
int numItems, i;
@ -251,8 +266,12 @@ static void window_multiplayer_groups_show_defaultgroup_dropdown(rct_window *w,
gDropdownItemsFormat[i] = 1142;
gDropdownItemsArgs[i] = network_get_group_name_string_id(i);
}
dropdown_set_checked(network_get_group_index(network_get_default_group()), true);
if (widget == &window_multiplayer_groups_widgets[WIDX_DEFAULT_GROUP_DROPDOWN]) {
dropdown_set_checked(network_get_group_index(network_get_default_group()), true);
} else
if (widget == &window_multiplayer_groups_widgets[WIDX_SELECTED_GROUP_DROPDOWN]) {
dropdown_set_checked(network_get_group_index(_selectedGroup), true);
}
}
static void window_multiplayer_players_mouseup(rct_window *w, int widgetIndex)
@ -268,7 +287,11 @@ static void window_multiplayer_players_resize(rct_window *w)
{
window_set_resize(w, 320, 124, 500, 450);
window_multiplayer_players_refresh_list(w);
w->no_list_items = network_get_num_players();
w->list_item_positions[0] = 0;
w->selected_list_item = -1;
window_invalidate(w);
}
static void window_multiplayer_players_mousedown(int widgetIndex, rct_window* w, rct_widget* widget)
@ -371,8 +394,6 @@ static void window_multiplayer_players_scrollpaint(rct_window *w, rct_drawpixeli
{
int y;
gfx_fill_rect(dpi, dpi->x, dpi->y, dpi->x + dpi->width - 1, dpi->y + dpi->height - 1, ColourMapA[w->colours[1]].mid_light);
y = 0;
for (int i = 0; i < network_get_num_players(); i++) {
if (y > dpi->y + dpi->height) {
@ -439,7 +460,13 @@ static void window_multiplayer_groups_mouseup(rct_window *w, int widgetIndex)
static void window_multiplayer_groups_resize(rct_window *w)
{
window_set_resize(w, 320, 61, 320, 61);
window_set_resize(w, 320, 200, 320, 500);
w->no_list_items = network_get_num_actions();
w->list_item_positions[0] = 0;
w->selected_list_item = -1;
window_invalidate(w);
}
static void window_multiplayer_groups_mousedown(int widgetIndex, rct_window* w, rct_widget* widget)
@ -452,7 +479,10 @@ static void window_multiplayer_groups_mousedown(int widgetIndex, rct_window* w,
}
break;
case WIDX_DEFAULT_GROUP_DROPDOWN:
window_multiplayer_groups_show_defaultgroup_dropdown(w, widget);
window_multiplayer_groups_show_group_dropdown(w, widget);
break;
case WIDX_SELECTED_GROUP_DROPDOWN:
window_multiplayer_groups_show_group_dropdown(w, widget);
break;
}
}
@ -462,9 +492,18 @@ static void window_multiplayer_groups_dropdown(rct_window *w, int widgetIndex, i
if (dropdownIndex == -1) {
return;
}
if (network_get_mode() == NETWORK_MODE_SERVER) {
network_set_default_group(network_get_group_id(dropdownIndex));
switch(widgetIndex){
case WIDX_DEFAULT_GROUP_DROPDOWN:
if (network_get_mode() == NETWORK_MODE_SERVER) {
network_set_default_group(network_get_group_id(dropdownIndex));
}
break;
case WIDX_SELECTED_GROUP_DROPDOWN:
_selectedGroup = network_get_group_id(dropdownIndex);
break;
}
window_invalidate(w);
}
@ -474,10 +513,61 @@ static void window_multiplayer_groups_update(rct_window *w)
widget_invalidate(w, WIDX_TAB1 + w->page);
}
static void window_multiplayer_groups_scrollgetsize(rct_window *w, int scrollIndex, int *width, int *height)
{
int i;
if (w->selected_list_item != -1) {
w->selected_list_item = -1;
window_invalidate(w);
}
*height = network_get_num_actions() * 10;
i = *height - window_multiplayer_groups_widgets[WIDX_LIST].bottom + window_multiplayer_groups_widgets[WIDX_LIST].top + 21;
if (i < 0)
i = 0;
if (i < w->scrolls[0].v_top) {
w->scrolls[0].v_top = i;
window_invalidate(w);
}
}
static void window_multiplayer_groups_scrollmousedown(rct_window *w, int scrollIndex, int x, int y)
{
int index;
index = y / 10;
if (index >= w->no_list_items)
return;
w->selected_list_item = index;
window_invalidate(w);
rct_widget *listWidget = &w->widgets[WIDX_LIST];
int ddx = w->x + listWidget->left + x - w->scrolls[0].h_left;
int ddy = w->y + listWidget->top + y - w->scrolls[0].v_top;
game_do_command(2 | (_selectedGroup << 8), GAME_COMMAND_FLAG_APPLY, index, 0, GAME_COMMAND_MODIFY_GROUPS, 0, 0);
}
static void window_multiplayer_groups_scrollmouseover(rct_window *w, int scrollIndex, int x, int y)
{
int index;
index = y / 10;
if (index >= w->no_list_items)
return;
w->selected_list_item = index;
window_invalidate(w);
}
static void window_multiplayer_groups_invalidate(rct_window *w)
{
window_multiplayer_set_pressed_tab(w);
window_multiplayer_anchor_border_widgets(w);
window_multiplayer_groups_widgets[WIDX_PERMISSIONS_LIST].right = w->width - 4;
window_multiplayer_groups_widgets[WIDX_PERMISSIONS_LIST].bottom = w->height - 0x0F;
window_align_tabs(w, WIDX_TAB1, WIDX_TAB2);
}
@ -500,16 +590,73 @@ static void window_multiplayer_groups_paint(rct_window *w, rct_drawpixelinfo *dp
);
}
gfx_draw_string_left(dpi, STR_DEFAULT_GROUP, NULL, w->colours[2], w->x + 6, 58 - 12 + w->y + 1);
int x = w->x + window_multiplayer_groups_widgets[WIDX_CONTENT_PANEL].left + 4;
int y = w->y + window_multiplayer_groups_widgets[WIDX_CONTENT_PANEL].top + 4;
gfx_draw_string_left(dpi, STR_DEFAULT_GROUP, NULL, w->colours[2], x, y);
y += 20;
gfx_fill_rect_inset(dpi, x, y - 6, x + 310, y - 5, w->colours[1], 32);
widget = &window_multiplayer_groups_widgets[WIDX_SELECTED_GROUP];
group = network_get_group_index(_selectedGroup);
if (group != -1) {
RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS, uint16) = network_get_group_name_string_id(group);
gfx_draw_string_centred(
dpi,
1193,
w->x + (widget->left + widget->right - 11) / 2,
w->y + widget->top,
0,
(void*)RCT2_ADDRESS_COMMON_FORMAT_ARGS
);
}
}
static void window_multiplayer_players_refresh_list(rct_window *w)
static void window_multiplayer_groups_scrollpaint(rct_window *w, rct_drawpixelinfo *dpi, int scrollIndex)
{
w->no_list_items = network_get_num_players();
w->list_item_positions[0] = 0;
int y = 0;
w->selected_list_item = -1;
window_invalidate(w);
gfx_fill_rect(dpi, dpi->x, dpi->y, dpi->x + dpi->width - 1, dpi->y + dpi->height - 1, ColourMapA[w->colours[1]].mid_light);
for (int i = 0; i < network_get_num_actions(); i++) {
if (i == w->selected_list_item) {
gfx_fill_rect(dpi, 0, y, 800, y + 9, 0x02000031);
}
if (y > dpi->y + dpi->height) {
break;
}
if (y + 11 >= dpi->y) {
char buffer[300];
char* lineCh;
int groupindex = network_get_group_index(_selectedGroup);
if (groupindex != -1){
if (network_can_perform_action(groupindex, i)) {
lineCh = buffer;
lineCh = utf8_write_codepoint(lineCh, FORMAT_WINDOW_COLOUR_2);
safe_strcpy(lineCh, "x", sizeof(buffer) - (lineCh - buffer));
gfx_draw_string(dpi, buffer, 0, 0, y - 1);
}
}
// Draw action name
RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS, uint16) = network_get_action_name_string_id(i);
gfx_draw_string_left(dpi, 1193, (void*)RCT2_ADDRESS_COMMON_FORMAT_ARGS, 0, 10, y - 1);
/*
char buffer[300];
char* lineCh;
lineCh = buffer;
int group = i;
lineCh = utf8_write_codepoint(lineCh, FORMAT_WINDOW_COLOUR_2);
safe_strcpy(lineCh, network_get_action_name(group), sizeof(buffer) - (lineCh - buffer));
gfx_clip_string(buffer, 80);
gfx_draw_string(dpi, buffer, 0, 0, y - 1);*/
}
y += 10;
}
}
static void window_multiplayer_draw_tab_image(rct_window *w, rct_drawpixelinfo *dpi, int page, int spriteIndex)