mirror of https://github.com/OpenRCT2/OpenRCT2.git
refactor and clean up twitch.cpp
This commit is contained in:
parent
770a37dd10
commit
f10d79c63c
|
@ -1,6 +1,7 @@
|
|||
#ifdef DISABLE_TWITCH
|
||||
|
||||
extern "C" {
|
||||
extern "C"
|
||||
{
|
||||
#include "twitch.h"
|
||||
}
|
||||
|
||||
|
@ -8,15 +9,19 @@
|
|||
|
||||
#else
|
||||
|
||||
// REQUIRES HTTP
|
||||
#ifdef DISABLE_HTTP
|
||||
#error HTTP must be enabled to use the TWITCH functionality.
|
||||
#endif
|
||||
|
||||
#include <vector>
|
||||
#include <SDL.h>
|
||||
|
||||
extern "C" {
|
||||
#include "../core/List.hpp"
|
||||
#include "../core/Math.hpp"
|
||||
#include "../core/String.hpp"
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include "../addresses.h"
|
||||
#include "../config.h"
|
||||
#include "../drawing/drawing.h"
|
||||
#include "../interface/console.h"
|
||||
#include "../localisation/localisation.h"
|
||||
#include "../management/news_item.h"
|
||||
|
@ -25,10 +30,14 @@ extern "C" {
|
|||
#include "../util/util.h"
|
||||
#include "http.h"
|
||||
#include "twitch.h"
|
||||
|
||||
}
|
||||
|
||||
enum {
|
||||
bool gTwitchEnable = false;
|
||||
|
||||
namespace Twitch
|
||||
{
|
||||
enum
|
||||
{
|
||||
TWITCH_STATE_JOINING,
|
||||
TWITCH_STATE_JOINED,
|
||||
TWITCH_STATE_WAITING,
|
||||
|
@ -36,55 +45,91 @@ enum {
|
|||
TWITCH_STATE_GET_MESSAGES,
|
||||
TWITCH_STATE_LEAVING,
|
||||
TWITCH_STATE_LEFT
|
||||
};
|
||||
};
|
||||
|
||||
// The time between HTTP requests.
|
||||
// TODO Ideally, the chat message pulse should be more frequent than the followers / chat members so that news messages etc.
|
||||
// have a lower latency.
|
||||
#define PULSE_TIME (10 * 1000)
|
||||
enum
|
||||
{
|
||||
TWITCH_STATUS_OK = 200
|
||||
};
|
||||
|
||||
const char *TwitchExtendedBaseUrl = "http://openrct.ursalabs.co/api/1/";
|
||||
struct AudienceMember
|
||||
{
|
||||
const char * Name;
|
||||
bool IsFollower;
|
||||
bool IsInChat;
|
||||
bool IsMod;
|
||||
bool Exists;
|
||||
bool ShouldTrack;
|
||||
|
||||
bool gTwitchEnable = false;
|
||||
static AudienceMember FromJson(json_t * json)
|
||||
{
|
||||
AudienceMember member = { 0 };
|
||||
|
||||
static int _twitchState = TWITCH_STATE_LEFT;
|
||||
static bool _twitchIdle = true;
|
||||
static uint32 _twitchLastPulseTick = 0;
|
||||
static int _twitchLastPulseOperation = 1;
|
||||
static http_json_response *_twitchJsonResponse;
|
||||
if (!json_is_object(json)) return member;
|
||||
json_t * name = json_object_get(json, "name");
|
||||
json_t * isFollower = json_object_get(json, "isFollower");
|
||||
json_t * isInChat = json_object_get(json, "inChat");
|
||||
json_t * isMod = json_object_get(json, "isMod");
|
||||
|
||||
static void twitch_join();
|
||||
static void twitch_leave();
|
||||
static void twitch_get_followers();
|
||||
static void twitch_get_messages();
|
||||
member.Name = json_string_value(name);
|
||||
member.IsFollower = json_boolean_value(isFollower);
|
||||
member.IsInChat = json_boolean_value(isInChat);
|
||||
member.IsMod = json_boolean_value(isMod);
|
||||
member.Exists = false;
|
||||
member.ShouldTrack = false;
|
||||
return member;
|
||||
}
|
||||
};
|
||||
|
||||
static void twitch_parse_followers();
|
||||
static void twitch_parse_messages();
|
||||
static void twitch_parse_chat_message(const char *message);
|
||||
/**
|
||||
* The time between HTTP requests.
|
||||
* TODO Ideally, the chat message pulse should be more frequent than the followers / chat members so that news messages etc.
|
||||
* have a lower latency.
|
||||
*/
|
||||
constexpr uint32 PulseTime = 10 * 1000;
|
||||
constexpr const char * TwitchExtendedBaseUrl = "http://openrct.ursalabs.co/api/1/";
|
||||
|
||||
void twitch_update()
|
||||
{
|
||||
if (!_twitchIdle)
|
||||
return;
|
||||
static int _twitchState = TWITCH_STATE_LEFT;
|
||||
static bool _twitchIdle = true;
|
||||
static uint32 _twitchLastPulseTick = 0;
|
||||
static int _twitchLastPulseOperation = 1;
|
||||
static http_json_response * _twitchJsonResponse;
|
||||
|
||||
bool twitchable =
|
||||
!(RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & (~SCREEN_FLAGS_PLAYING)) &&
|
||||
gConfigTwitch.channel != NULL &&
|
||||
gConfigTwitch.channel[0] != 0 &&
|
||||
gTwitchEnable;
|
||||
static void Join();
|
||||
static void Leave();
|
||||
static void GetFollowers();
|
||||
static void GetMessages();
|
||||
static void ParseFollowers();
|
||||
static void ParseMessages();
|
||||
static bool ShouldTrackMember(const AudienceMember * member);
|
||||
static bool ShouldMemberBeGuest(const AudienceMember * member);
|
||||
static void ManageGuestNames(List<AudienceMember> &members);
|
||||
static void ParseChatMessage(const char * message);
|
||||
static void DoChatMessageNews(const char * message);
|
||||
|
||||
if (twitchable) {
|
||||
if (RCT2_GLOBAL(RCT2_ADDRESS_GAME_PAUSED, uint8) != 0)
|
||||
return;
|
||||
static bool IsTwitchEnabled()
|
||||
{
|
||||
if (!gTwitchEnable) return false;
|
||||
if (RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & (~SCREEN_FLAGS_PLAYING)) return false;
|
||||
if (String::IsNullOrEmpty(gConfigTwitch.channel)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void Update()
|
||||
{
|
||||
if (!_twitchIdle) return;
|
||||
if (IsTwitchEnabled()) {
|
||||
if (RCT2_GLOBAL(RCT2_ADDRESS_GAME_PAUSED, uint8) != 0) return;
|
||||
|
||||
switch (_twitchState) {
|
||||
case TWITCH_STATE_LEFT:
|
||||
{
|
||||
uint32 currentTime = SDL_GetTicks();
|
||||
uint32 timeSinceLastPulse = currentTime - _twitchLastPulseTick;
|
||||
if (_twitchLastPulseTick == 0 || timeSinceLastPulse > PULSE_TIME) {
|
||||
if (_twitchLastPulseTick == 0 || timeSinceLastPulse > PulseTime)
|
||||
{
|
||||
_twitchLastPulseTick = currentTime;
|
||||
twitch_join();
|
||||
Join();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -92,41 +137,47 @@ void twitch_update()
|
|||
{
|
||||
uint32 currentTime = SDL_GetTicks();
|
||||
uint32 timeSinceLastPulse = currentTime - _twitchLastPulseTick;
|
||||
if (_twitchLastPulseTick == 0 || timeSinceLastPulse > PULSE_TIME) {
|
||||
if (_twitchLastPulseTick == 0 || timeSinceLastPulse > PulseTime) {
|
||||
_twitchLastPulseTick = currentTime;
|
||||
_twitchLastPulseOperation = (_twitchLastPulseOperation + 1) % 2;
|
||||
switch (_twitchLastPulseOperation + TWITCH_STATE_GET_FOLLOWERS) {
|
||||
case TWITCH_STATE_GET_FOLLOWERS:
|
||||
twitch_get_followers();
|
||||
GetFollowers();
|
||||
break;
|
||||
case TWITCH_STATE_GET_MESSAGES:
|
||||
if (gConfigTwitch.enable_news)
|
||||
twitch_get_messages();
|
||||
{
|
||||
GetMessages();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TWITCH_STATE_GET_FOLLOWERS:
|
||||
twitch_parse_followers();
|
||||
ParseFollowers();
|
||||
break;
|
||||
case TWITCH_STATE_GET_MESSAGES:
|
||||
twitch_parse_messages();
|
||||
ParseMessages();
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (_twitchState != TWITCH_STATE_LEFT)
|
||||
twitch_leave();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_twitchState != TWITCH_STATE_LEFT)
|
||||
{
|
||||
Leave();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* GET /leave/:join
|
||||
*/
|
||||
static void twitch_join()
|
||||
{
|
||||
static void Join()
|
||||
{
|
||||
char url[256];
|
||||
sprintf(url, "%sjoin/%s", TwitchExtendedBaseUrl, gConfigTwitch.channel);
|
||||
snprintf(url, sizeof(url), "%sjoin/%s", TwitchExtendedBaseUrl, gConfigTwitch.channel);
|
||||
|
||||
_twitchState = TWITCH_STATE_JOINING;
|
||||
_twitchIdle = false;
|
||||
|
@ -134,17 +185,25 @@ static void twitch_join()
|
|||
http_json_request request;
|
||||
request.url = url;
|
||||
request.method = HTTP_METHOD_GET;
|
||||
request.body = NULL;
|
||||
http_request_json_async(&request, [](http_json_response *jsonResponse) -> void {
|
||||
if (jsonResponse == NULL) {
|
||||
request.body = nullptr;
|
||||
http_request_json_async(&request, [](http_json_response *jsonResponse) -> void
|
||||
{
|
||||
if (jsonResponse == nullptr)
|
||||
{
|
||||
_twitchState = TWITCH_STATE_LEFT;
|
||||
console_writeline("Unable to connect to twitch channel.");
|
||||
} else {
|
||||
json_t *jsonStatus = json_object_get(jsonResponse->root, "status");
|
||||
if (json_is_number(jsonStatus) && json_integer_value(jsonStatus) == 200)
|
||||
_twitchState = TWITCH_STATE_JOINED;
|
||||
}
|
||||
else
|
||||
{
|
||||
json_t * jsonStatus = json_object_get(jsonResponse->root, "status");
|
||||
if (json_is_number(jsonStatus) && json_integer_value(jsonStatus) == TWITCH_STATUS_OK)
|
||||
{
|
||||
_twitchState = TWITCH_STATE_JOINED;
|
||||
}
|
||||
else
|
||||
{
|
||||
_twitchState = TWITCH_STATE_LEFT;
|
||||
}
|
||||
|
||||
http_request_json_dispose(jsonResponse);
|
||||
|
||||
|
@ -153,16 +212,17 @@ static void twitch_join()
|
|||
}
|
||||
_twitchIdle = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* GET /leave/:channel
|
||||
*/
|
||||
static void twitch_leave()
|
||||
{
|
||||
if (_twitchJsonResponse != NULL) {
|
||||
static void Leave()
|
||||
{
|
||||
if (_twitchJsonResponse != nullptr)
|
||||
{
|
||||
http_request_json_dispose(_twitchJsonResponse);
|
||||
_twitchJsonResponse = NULL;
|
||||
_twitchJsonResponse = nullptr;
|
||||
}
|
||||
|
||||
console_writeline("Left twitch channel.");
|
||||
|
@ -174,25 +234,26 @@ static void twitch_leave()
|
|||
|
||||
// HTTP request no longer used as it could be abused
|
||||
// char url[256];
|
||||
// sprintf(url, "%sleave/%s", TwitchExtendedBaseUrl, gConfigTwitch.channel);
|
||||
// snprintf(url, sizeof(url), "%sleave/%s", TwitchExtendedBaseUrl, gConfigTwitch.channel);
|
||||
// _twitchState = TWITCH_STATE_LEAVING;
|
||||
// _twitchIdle = false;
|
||||
// http_request_json_async(url, [](http_json_response *jsonResponse) -> void {
|
||||
// http_request_json_async(url, [](http_json_response * jsonResponse) -> void
|
||||
// {
|
||||
// http_request_json_dispose(jsonResponse);
|
||||
// _twitchState = TWITCH_STATE_LEFT;
|
||||
// _twitchIdle = true;
|
||||
//
|
||||
// console_writeline("Left twitch channel.");
|
||||
// });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* GET /channel/:channel/audience
|
||||
*/
|
||||
static void twitch_get_followers()
|
||||
{
|
||||
static void GetFollowers()
|
||||
{
|
||||
char url[256];
|
||||
sprintf(url, "%schannel/%s/audience", TwitchExtendedBaseUrl, gConfigTwitch.channel);
|
||||
snprintf(url, sizeof(url), "%schannel/%s/audience", TwitchExtendedBaseUrl, gConfigTwitch.channel);
|
||||
|
||||
_twitchState = TWITCH_STATE_WAITING;
|
||||
_twitchIdle = false;
|
||||
|
@ -201,24 +262,28 @@ static void twitch_get_followers()
|
|||
request.url = url;
|
||||
request.method = HTTP_METHOD_GET;
|
||||
request.body = NULL;
|
||||
http_request_json_async(&request, [](http_json_response *jsonResponse) -> void {
|
||||
if (jsonResponse == NULL) {
|
||||
http_request_json_async(&request, [](http_json_response * jsonResponse) -> void
|
||||
{
|
||||
if (jsonResponse == nullptr)
|
||||
{
|
||||
_twitchState = TWITCH_STATE_JOINED;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
_twitchJsonResponse = jsonResponse;
|
||||
_twitchState = TWITCH_STATE_GET_FOLLOWERS;
|
||||
}
|
||||
_twitchIdle = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* GET /channel/:channel/messages
|
||||
*/
|
||||
static void twitch_get_messages()
|
||||
{
|
||||
static void GetMessages()
|
||||
{
|
||||
char url[256];
|
||||
sprintf(url, "%schannel/%s/messages", TwitchExtendedBaseUrl, gConfigTwitch.channel);
|
||||
snprintf(url, sizeof(url), "%schannel/%s/messages", TwitchExtendedBaseUrl, gConfigTwitch.channel);
|
||||
|
||||
_twitchState = TWITCH_STATE_WAITING;
|
||||
_twitchIdle = false;
|
||||
|
@ -226,135 +291,45 @@ static void twitch_get_messages()
|
|||
http_json_request request;
|
||||
request.url = url;
|
||||
request.method = HTTP_METHOD_GET;
|
||||
request.body = NULL;
|
||||
http_request_json_async(&request, [](http_json_response *jsonResponse) -> void {
|
||||
if (jsonResponse == NULL) {
|
||||
request.body = nullptr;
|
||||
http_request_json_async(&request, [](http_json_response * jsonResponse) -> void
|
||||
{
|
||||
if (jsonResponse == nullptr)
|
||||
{
|
||||
_twitchState = TWITCH_STATE_JOINED;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
_twitchJsonResponse = jsonResponse;
|
||||
_twitchState = TWITCH_STATE_GET_MESSAGES;
|
||||
}
|
||||
_twitchIdle = true;
|
||||
});
|
||||
}
|
||||
|
||||
static void twitch_parse_followers()
|
||||
{
|
||||
struct AudienceMember {
|
||||
const char *name;
|
||||
bool isFollower;
|
||||
bool isInChat;
|
||||
bool isMod;
|
||||
bool exists;
|
||||
bool shouldTrack;
|
||||
};
|
||||
|
||||
std::vector<AudienceMember> members;
|
||||
}
|
||||
|
||||
static void ParseFollowers()
|
||||
{
|
||||
http_json_response *jsonResponse = _twitchJsonResponse;
|
||||
if (json_is_array(jsonResponse->root)) {
|
||||
int audienceCount = json_array_size(jsonResponse->root);
|
||||
for (int i = 0; i < audienceCount; i++) {
|
||||
json_t *audienceMember = json_array_get(jsonResponse->root, i);
|
||||
if (!json_is_object(audienceMember))
|
||||
continue;
|
||||
if (json_is_array(jsonResponse->root))
|
||||
{
|
||||
List<AudienceMember> members;
|
||||
|
||||
json_t *name = json_object_get(audienceMember, "name");
|
||||
json_t *isFollower = json_object_get(audienceMember, "isFollower");
|
||||
json_t *isInChat = json_object_get(audienceMember, "inChat");
|
||||
json_t *isMod = json_object_get(audienceMember, "isMod");
|
||||
|
||||
AudienceMember member;
|
||||
member.name = json_string_value(name);
|
||||
member.isFollower = json_boolean_value(isFollower);
|
||||
member.isInChat = json_boolean_value(isInChat);
|
||||
member.isMod = json_boolean_value(isMod);
|
||||
member.exists = false;
|
||||
member.shouldTrack = false;
|
||||
|
||||
if (member.name == NULL || member.name[0] == 0)
|
||||
continue;
|
||||
|
||||
if (member.isInChat && gConfigTwitch.enable_chat_peep_tracking)
|
||||
member.shouldTrack = true;
|
||||
else if (member.isFollower && gConfigTwitch.enable_follower_peep_tracking)
|
||||
member.shouldTrack = true;
|
||||
|
||||
if (gConfigTwitch.enable_chat_peep_names && member.isInChat)
|
||||
members.push_back(member);
|
||||
else if (gConfigTwitch.enable_follower_peep_names && member.isFollower)
|
||||
members.push_back(member);
|
||||
}
|
||||
|
||||
uint16 spriteIndex;
|
||||
rct_peep *peep;
|
||||
char buffer[256];
|
||||
|
||||
// Check what followers are already in the park
|
||||
FOR_ALL_GUESTS(spriteIndex, peep) {
|
||||
if (is_user_string_id(peep->name_string_idx)) {
|
||||
format_string(buffer, peep->name_string_idx, NULL);
|
||||
|
||||
AudienceMember *member = NULL;
|
||||
for (size_t i = 0; i < members.size(); i++) {
|
||||
if (_strcmpi(buffer, members[i].name) == 0) {
|
||||
member = &members[i];
|
||||
members[i].exists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (peep->peep_flags & PEEP_FLAGS_TWITCH) {
|
||||
if (member == NULL) {
|
||||
// Member no longer peep name worthy
|
||||
peep->peep_flags &= ~(PEEP_FLAGS_TRACKING | PEEP_FLAGS_TWITCH);
|
||||
|
||||
// TODO set peep name back to number / real name
|
||||
} else {
|
||||
if (member->shouldTrack)
|
||||
peep->peep_flags |= (PEEP_FLAGS_TRACKING);
|
||||
else if (!member->shouldTrack)
|
||||
peep->peep_flags &= ~(PEEP_FLAGS_TRACKING);
|
||||
}
|
||||
} else if (member != NULL && !(peep->peep_flags & PEEP_FLAGS_LEAVING_PARK)) {
|
||||
// Peep with same name already exists but not twitch
|
||||
peep->peep_flags |= PEEP_FLAGS_TWITCH;
|
||||
if (member->shouldTrack)
|
||||
peep->peep_flags |= PEEP_FLAGS_TRACKING;
|
||||
size_t audienceCount = json_array_size(jsonResponse->root);
|
||||
for (size_t i = 0; i < audienceCount; i++)
|
||||
{
|
||||
json_t * jsonAudienceMember = json_array_get(jsonResponse->root, i);
|
||||
auto member = AudienceMember::FromJson(jsonAudienceMember);
|
||||
if (!String::IsNullOrEmpty(member.Name))
|
||||
{
|
||||
member.ShouldTrack = ShouldTrackMember(&member);
|
||||
if (ShouldMemberBeGuest(&member))
|
||||
{
|
||||
members.Add(member);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Rename non-named peeps to followers that aren't currently in the park.
|
||||
if (members.size() > 0) {
|
||||
int memberIndex = -1;
|
||||
FOR_ALL_GUESTS(spriteIndex, peep) {
|
||||
int originalMemberIndex = memberIndex;
|
||||
for (size_t i = memberIndex + 1; i < members.size(); i++) {
|
||||
if (!members[i].exists) {
|
||||
memberIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (originalMemberIndex == memberIndex)
|
||||
break;
|
||||
|
||||
AudienceMember *member = &members[memberIndex];
|
||||
if (!is_user_string_id(peep->name_string_idx) && !(peep->peep_flags & PEEP_FLAGS_LEAVING_PARK)) {
|
||||
// Rename peep and add flags
|
||||
rct_string_id newStringId = user_string_allocate(4, member->name);
|
||||
if (newStringId != 0) {
|
||||
peep->name_string_idx = newStringId;
|
||||
peep->peep_flags |= PEEP_FLAGS_TWITCH;
|
||||
if (member->shouldTrack)
|
||||
peep->peep_flags |= PEEP_FLAGS_TRACKING;
|
||||
}
|
||||
} else {
|
||||
// Peep still yet to be found for member
|
||||
memberIndex--;
|
||||
}
|
||||
}
|
||||
}
|
||||
ManageGuestNames(members);
|
||||
}
|
||||
|
||||
http_request_json_dispose(_twitchJsonResponse);
|
||||
|
@ -362,79 +337,227 @@ static void twitch_parse_followers()
|
|||
_twitchState = TWITCH_STATE_JOINED;
|
||||
|
||||
gfx_invalidate_screen();
|
||||
}
|
||||
}
|
||||
|
||||
static void twitch_parse_messages()
|
||||
{
|
||||
http_json_response *jsonResponse = _twitchJsonResponse;
|
||||
if (json_is_array(jsonResponse->root)) {
|
||||
int messageCount = json_array_size(jsonResponse->root);
|
||||
for (int i = 0; i < messageCount; i++) {
|
||||
json_t *jsonMessage = json_array_get(jsonResponse->root, i);
|
||||
static void ParseMessages()
|
||||
{
|
||||
http_json_response * jsonResponse = _twitchJsonResponse;
|
||||
if (json_is_array(jsonResponse->root))
|
||||
{
|
||||
size_t messageCount = json_array_size(jsonResponse->root);
|
||||
for (size_t i = 0; i < messageCount; i++) {
|
||||
json_t * jsonMessage = json_array_get(jsonResponse->root, i);
|
||||
if (!json_is_object(jsonMessage))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
json_t *jsonText = json_object_get(jsonMessage, "message");
|
||||
const char *text = json_string_value(jsonText);
|
||||
|
||||
twitch_parse_chat_message(text);
|
||||
json_t * jsonText = json_object_get(jsonMessage, "message");
|
||||
const char * text = json_string_value(jsonText);
|
||||
ParseChatMessage(text);
|
||||
}
|
||||
}
|
||||
|
||||
http_request_json_dispose(_twitchJsonResponse);
|
||||
_twitchJsonResponse = NULL;
|
||||
_twitchJsonResponse = nullptr;
|
||||
_twitchState = TWITCH_STATE_JOINED;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
static bool ShouldTrackMember(const AudienceMember * member)
|
||||
{
|
||||
if (member->IsInChat && gConfigTwitch.enable_chat_peep_tracking)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (member->IsFollower && gConfigTwitch.enable_follower_peep_tracking)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool ShouldMemberBeGuest(const AudienceMember * member)
|
||||
{
|
||||
if (gConfigTwitch.enable_chat_peep_names && member->IsInChat)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (gConfigTwitch.enable_follower_peep_names && member->IsFollower)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void ManageGuestNames(List<AudienceMember> &members)
|
||||
{
|
||||
// Check what followers are already in the park
|
||||
uint16 spriteIndex;
|
||||
rct_peep *peep;
|
||||
FOR_ALL_GUESTS(spriteIndex, peep)
|
||||
{
|
||||
if (is_user_string_id(peep->name_string_idx))
|
||||
{
|
||||
utf8 buffer[256];
|
||||
format_string(buffer, peep->name_string_idx, NULL);
|
||||
|
||||
AudienceMember * member = nullptr;
|
||||
for (AudienceMember &member : members)
|
||||
{
|
||||
if (String::Equals(buffer, member.Name, true))
|
||||
{
|
||||
member.Exists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (peep->peep_flags & PEEP_FLAGS_TWITCH)
|
||||
{
|
||||
if (member == nullptr)
|
||||
{
|
||||
// Member no longer peep name worthy
|
||||
peep->peep_flags &= ~(PEEP_FLAGS_TRACKING | PEEP_FLAGS_TWITCH);
|
||||
|
||||
// TODO set peep name back to number / real name
|
||||
}
|
||||
else
|
||||
{
|
||||
if (member->ShouldTrack)
|
||||
{
|
||||
peep->peep_flags |= (PEEP_FLAGS_TRACKING);
|
||||
}
|
||||
else if (!member->ShouldTrack)
|
||||
{
|
||||
peep->peep_flags &= ~(PEEP_FLAGS_TRACKING);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (member != nullptr && !(peep->peep_flags & PEEP_FLAGS_LEAVING_PARK))
|
||||
{
|
||||
// Peep with same name already exists but not twitch
|
||||
peep->peep_flags |= PEEP_FLAGS_TWITCH;
|
||||
if (member->ShouldTrack)
|
||||
{
|
||||
peep->peep_flags |= PEEP_FLAGS_TRACKING;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Rename non-named peeps to followers that aren't currently in the park.
|
||||
if (members.GetCount() > 0)
|
||||
{
|
||||
size_t memberIndex = SIZE_MAX;
|
||||
FOR_ALL_GUESTS(spriteIndex, peep)
|
||||
{
|
||||
int originalMemberIndex = memberIndex;
|
||||
for (size_t i = memberIndex + 1; i < members.GetCount(); i++)
|
||||
{
|
||||
if (!members[i].Exists)
|
||||
{
|
||||
memberIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (originalMemberIndex == memberIndex)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
AudienceMember * member = &members[memberIndex];
|
||||
if (!is_user_string_id(peep->name_string_idx) && !(peep->peep_flags & PEEP_FLAGS_LEAVING_PARK))
|
||||
{
|
||||
// Rename peep and add flags
|
||||
rct_string_id newStringId = user_string_allocate(4, member->Name);
|
||||
if (newStringId != 0)
|
||||
{
|
||||
peep->name_string_idx = newStringId;
|
||||
peep->peep_flags |= PEEP_FLAGS_TWITCH;
|
||||
if (member->ShouldTrack)
|
||||
{
|
||||
peep->peep_flags |= PEEP_FLAGS_TRACKING;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Peep still yet to be found for member
|
||||
memberIndex--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Like strchr but allows searching for one of many characters.
|
||||
*/
|
||||
static char *strchrm(const char *str, const char *find)
|
||||
{
|
||||
const char *result = NULL;
|
||||
do {
|
||||
const char *fch = find;
|
||||
while (*fch != 0) {
|
||||
static char * strchrm(const char * str, const char * find)
|
||||
{
|
||||
const char * result = nullptr;
|
||||
do
|
||||
{
|
||||
const char * fch = find;
|
||||
while (*fch != '\0')
|
||||
{
|
||||
if (*str == *fch)
|
||||
return (char*)str;
|
||||
|
||||
{
|
||||
return (char *)str;
|
||||
}
|
||||
fch++;
|
||||
}
|
||||
} while (*str++ != 0);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
while (*str++ != '\0');
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static char *strskipwhitespace(const char *str)
|
||||
{
|
||||
static char * strskipwhitespace(const char * str)
|
||||
{
|
||||
while (*str == ' ' || *str == '\t')
|
||||
{
|
||||
str++;
|
||||
}
|
||||
return (char *)str;
|
||||
}
|
||||
|
||||
return (char*)str;
|
||||
}
|
||||
|
||||
static void twitch_parse_chat_message(const char *message)
|
||||
{
|
||||
char buffer[256], *ch;
|
||||
|
||||
static void ParseChatMessage(const char * message)
|
||||
{
|
||||
message = strskipwhitespace(message);
|
||||
if (message[0] != '!')
|
||||
if (!String::StartsWith(message, "!"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip '!'
|
||||
message++;
|
||||
ch = strchrm(message, " \t");
|
||||
safe_strcpy(buffer, message, ch - message + 1);
|
||||
if (_strcmpi(buffer, "news") == 0) {
|
||||
if (gConfigTwitch.enable_news) {
|
||||
|
||||
// Set buffer to the next word / token and skip
|
||||
char buffer[32];
|
||||
const char * ch = strchrm(message, " \t");
|
||||
safe_strcpy(buffer, message, Math::Min(sizeof(buffer), (size_t)(ch - message + 1)));
|
||||
ch = strskipwhitespace(ch);
|
||||
|
||||
buffer[0] = (char)FORMAT_TOPAZ;
|
||||
safe_strcpy(buffer + 1, ch, sizeof(buffer) - 2);
|
||||
// Check what the word / token is
|
||||
if (String::Equals(buffer, "news", true))
|
||||
{
|
||||
DoChatMessageNews(ch);
|
||||
}
|
||||
}
|
||||
|
||||
static void DoChatMessageNews(const char * message)
|
||||
{
|
||||
if (gConfigTwitch.enable_news)
|
||||
{
|
||||
utf8 buffer[256];
|
||||
buffer[0] = (utf8)FORMAT_TOPAZ;
|
||||
safe_strcpy(buffer + 1, message, sizeof(buffer) - 1);
|
||||
|
||||
// Remove unsupport characters
|
||||
// TODO allow when OpenRCT2 gains unicode support
|
||||
ch = buffer;
|
||||
while (ch[0] != 0) {
|
||||
if ((unsigned char)ch[0] < 32 || (unsigned char)ch[0] > 122) {
|
||||
char * ch = buffer + 1;
|
||||
while (ch[0] != '\0')
|
||||
{
|
||||
if ((unsigned char)ch[0] < 32 || (unsigned char)ch[0] > 122)
|
||||
{
|
||||
ch[0] = ' ';
|
||||
}
|
||||
ch++;
|
||||
|
@ -446,4 +569,9 @@ static void twitch_parse_chat_message(const char *message)
|
|||
}
|
||||
}
|
||||
|
||||
void twitch_update()
|
||||
{
|
||||
Twitch::Update();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue