2020-08-18 20:42:30 +00:00
# include "CNShardServer.hpp"
# include "CNStructs.hpp"
# include "ChatManager.hpp"
# include "PlayerManager.hpp"
2020-10-03 15:21:36 +00:00
# include "TransportManager.hpp"
2020-10-03 02:05:20 +00:00
# include "TableData.hpp"
2020-10-06 22:34:11 +00:00
# include "limits.h"
2020-08-18 20:42:30 +00:00
2020-10-02 23:50:47 +00:00
# include <sstream>
# include <iterator>
2020-10-06 22:34:11 +00:00
# include <cmath>
2020-10-02 23:50:47 +00:00
std : : map < std : : string , ChatCommand > ChatManager : : commands ;
std : : vector < std : : string > parseArgs ( std : : string full ) {
std : : stringstream ss ( full ) ;
std : : istream_iterator < std : : string > begin ( ss ) ;
std : : istream_iterator < std : : string > end ;
return std : : vector < std : : string > ( begin , end ) ;
}
2020-10-03 00:20:59 +00:00
bool runCmd ( std : : string full , CNSocket * sock ) {
std : : vector < std : : string > args = parseArgs ( full ) ;
2020-10-02 23:50:47 +00:00
std : : string cmd = args [ 0 ] . substr ( 1 , args [ 0 ] . size ( ) - 1 ) ;
2020-10-05 00:03:13 +00:00
2020-10-02 23:59:07 +00:00
// check if the command exists
2020-10-02 23:50:47 +00:00
if ( ChatManager : : commands . find ( cmd ) ! = ChatManager : : commands . end ( ) ) {
Player * plr = PlayerManager : : getPlayer ( sock ) ;
ChatCommand command = ChatManager : : commands [ cmd ] ;
2020-10-05 00:03:13 +00:00
2020-10-02 23:59:07 +00:00
// sanity check + does the player have the required account level to use the command?
2020-10-02 23:50:47 +00:00
if ( plr ! = nullptr & & plr - > accountLevel < = command . requiredAccLevel ) {
command . handlr ( full , args , sock ) ;
return true ;
} else {
ChatManager : : sendServerMessage ( sock , " You don't have access to that command! " ) ;
return false ;
}
}
ChatManager : : sendServerMessage ( sock , " Unknown command! " ) ;
return false ;
}
2020-10-03 00:20:59 +00:00
void testCommand ( std : : string full , std : : vector < std : : string > & args , CNSocket * sock ) {
2020-10-02 23:59:07 +00:00
ChatManager : : sendServerMessage ( sock , " Test command is working! Here are your passed args: " ) ;
for ( std : : string arg : args ) {
ChatManager : : sendServerMessage ( sock , arg ) ;
}
2020-10-02 23:50:47 +00:00
}
2020-10-03 00:20:59 +00:00
void accessCommand ( std : : string full , std : : vector < std : : string > & args , CNSocket * sock ) {
2020-10-02 23:50:47 +00:00
ChatManager : : sendServerMessage ( sock , " Your access level is " + std : : to_string ( PlayerManager : : getPlayer ( sock ) - > accountLevel ) ) ;
}
2020-10-05 00:33:30 +00:00
void populationCommand ( std : : string full , std : : vector < std : : string > & args , CNSocket * sock ) {
ChatManager : : sendServerMessage ( sock , std : : to_string ( PlayerManager : : players . size ( ) ) + " players online " ) ;
}
void levelCommand ( std : : string full , std : : vector < std : : string > & args , CNSocket * sock ) {
Player * plr = PlayerManager : : getPlayer ( sock ) ;
if ( plr = = nullptr )
return ;
char * tmp ;
int level = std : : strtol ( args [ 1 ] . c_str ( ) , & tmp , 10 ) ;
if ( * tmp )
return ;
if ( ( level < 1 | | level > 36 ) & & plr - > accountLevel > 30 )
return ;
if ( ! ( level < 1 | | level > 36 ) )
plr - > level = level ;
INITSTRUCT ( sP_FE2CL_REP_PC_CHANGE_LEVEL , resp ) ;
resp . iPC_ID = plr - > iID ;
resp . iPC_Level = level ;
sock - > sendPacket ( ( void * ) & resp , P_FE2CL_REP_PC_CHANGE_LEVEL , sizeof ( sP_FE2CL_REP_PC_CHANGE_LEVEL ) ) ;
PlayerManager : : sendToViewable ( sock , ( void * ) & resp , P_FE2CL_REP_PC_CHANGE_LEVEL , sizeof ( sP_FE2CL_REP_PC_CHANGE_LEVEL ) ) ;
}
2020-10-03 02:05:20 +00:00
void mssCommand ( std : : string full , std : : vector < std : : string > & args , CNSocket * sock ) {
if ( args . size ( ) < 2 ) {
ChatManager : : sendServerMessage ( sock , " [MSS] Too few arguments " ) ;
2020-10-03 15:21:36 +00:00
ChatManager : : sendServerMessage ( sock , " [MSS] Usage: /mss <route> <add/remove/goto/clear/test/export> <<height>> " ) ;
2020-10-03 02:05:20 +00:00
return ;
}
// Validate route number
char * routeNumC ;
int routeNum = std : : strtol ( args [ 1 ] . c_str ( ) , & routeNumC , 10 ) ;
if ( * routeNumC ) {
// not an integer
ChatManager : : sendServerMessage ( sock , " [MSS] Invalid route number ' " + args [ 1 ] + " ' " ) ;
return ;
}
if ( args . size ( ) < 3 ) {
ChatManager : : sendServerMessage ( sock , " [MSS] Too few arguments " ) ;
2020-10-06 19:53:21 +00:00
ChatManager : : sendServerMessage ( sock , " [MSS] Usage: /mss <route> <add/remove/goto/clear/test> <<height>> " ) ;
2020-10-03 02:05:20 +00:00
return ;
}
// get the route (if it doesn't exist yet, this will also make it)
2020-10-03 15:21:36 +00:00
std : : vector < WarpLocation > * route = & TableData : : RunningSkywayRoutes [ routeNum ] ;
2020-10-03 02:05:20 +00:00
// mss <route> add <height>
if ( args [ 2 ] = = " add " ) {
// make sure height token exists
if ( args . size ( ) < 4 ) {
ChatManager : : sendServerMessage ( sock , " [MSS] Point height must be specified " ) ;
ChatManager : : sendServerMessage ( sock , " [MSS] Usage: /mss <route> add <height> " ) ;
return ;
}
// validate height token
char * heightC ;
int height = std : : strtol ( args [ 3 ] . c_str ( ) , & heightC , 10 ) ;
if ( * heightC ) {
ChatManager : : sendServerMessage ( sock , " [MSS] Invalid height " + args [ 3 ] ) ;
return ;
}
Player * plr = PlayerManager : : getPlayer ( sock ) ;
2020-10-03 15:21:36 +00:00
route - > push_back ( { plr - > x , plr - > y , height } ) ; // add point
2020-10-03 02:05:20 +00:00
ChatManager : : sendServerMessage ( sock , " [MSS] Added point ( " + std : : to_string ( plr - > x ) + " , " + std : : to_string ( plr - > y ) + " , " + std : : to_string ( height ) + " ) to route " + std : : to_string ( routeNum ) ) ;
return ;
}
// mss <route> remove
if ( args [ 2 ] = = " remove " ) {
if ( route - > empty ( ) ) {
ChatManager : : sendServerMessage ( sock , " [MSS] Route " + std : : to_string ( routeNum ) + " is empty " ) ;
return ;
}
2020-10-03 15:21:36 +00:00
WarpLocation pulled = route - > back ( ) ;
route - > pop_back ( ) ; // remove point at top of stack
2020-10-03 02:05:20 +00:00
ChatManager : : sendServerMessage ( sock , " [MSS] Removed point ( " + std : : to_string ( pulled . x ) + " , " + std : : to_string ( pulled . y ) + " , " + std : : to_string ( pulled . z ) + " ) from route " + std : : to_string ( routeNum ) ) ;
return ;
}
// mss <route> goto
if ( args [ 2 ] = = " goto " ) {
if ( route - > empty ( ) ) {
ChatManager : : sendServerMessage ( sock , " [MSS] Route " + std : : to_string ( routeNum ) + " is empty " ) ;
return ;
}
2020-10-03 15:21:36 +00:00
WarpLocation pulled = route - > back ( ) ;
PlayerManager : : sendPlayerTo ( sock , pulled . x , pulled . y , pulled . z ) ;
2020-10-03 02:05:20 +00:00
return ;
}
// mss <route> clear
if ( args [ 2 ] = = " clear " ) {
2020-10-03 15:21:36 +00:00
route - > clear ( ) ;
2020-10-03 02:05:20 +00:00
ChatManager : : sendServerMessage ( sock , " [MSS] Cleared route " + std : : to_string ( routeNum ) ) ;
return ;
}
2020-10-06 21:59:33 +00:00
// mss <route> test
2020-10-03 15:21:36 +00:00
if ( args [ 2 ] = = " test " ) {
if ( route - > empty ( ) ) {
ChatManager : : sendServerMessage ( sock , " [MSS] Route " + std : : to_string ( routeNum ) + " is empty " ) ;
return ;
}
2020-10-06 19:53:21 +00:00
WarpLocation pulled = route - > front ( ) ;
PlayerManager : : sendPlayerTo ( sock , pulled . x , pulled . y , pulled . z ) ;
TransportManager : : testMssRoute ( sock , route ) ;
2020-10-03 02:05:20 +00:00
return ;
}
2020-10-06 19:53:21 +00:00
// for compatibility: mss <route> export
2020-10-03 02:05:20 +00:00
if ( args [ 2 ] = = " export " ) {
2020-10-06 21:59:33 +00:00
ChatManager : : sendServerMessage ( sock , " Wrote gruntwork to " + settings : : GRUNTWORKJSON ) ;
2020-10-06 19:53:21 +00:00
TableData : : flush ( ) ;
2020-10-03 02:05:20 +00:00
return ;
}
// mss ????
ChatManager : : sendServerMessage ( sock , " [MSS] Unknown command ' " + args [ 2 ] + " ' " ) ;
}
2020-10-06 21:59:33 +00:00
void npcRotateCommand ( std : : string full , std : : vector < std : : string > & args , CNSocket * sock ) {
2020-10-06 22:34:11 +00:00
PlayerView & plrv = PlayerManager : : players [ sock ] ;
Player * plr = plrv . plr ;
2020-10-06 21:59:33 +00:00
2020-10-06 22:34:11 +00:00
BaseNPC * npc = nullptr ;
int lastDist = INT_MAX ;
for ( auto c = plrv . currentChunks . begin ( ) ; c ! = plrv . currentChunks . end ( ) ; c + + ) { // haha get it
Chunk * chunk = * c ;
for ( auto _npc = chunk - > NPCs . begin ( ) ; _npc ! = chunk - > NPCs . end ( ) ; _npc + + ) {
BaseNPC * npcTemp = NPCManager : : NPCs [ * _npc ] ;
int distXY = std : : hypot ( plr - > x - npcTemp - > appearanceData . iX , plr - > y - npcTemp - > appearanceData . iY ) ;
int dist = std : : hypot ( distXY , plr - > z - npcTemp - > appearanceData . iZ ) ;
if ( dist < lastDist ) {
npc = npcTemp ;
lastDist = dist ;
}
}
2020-10-06 21:59:33 +00:00
}
2020-10-06 22:34:11 +00:00
if ( npc = = nullptr ) {
ChatManager : : sendServerMessage ( sock , " [NPCR] No NPCs found nearby " ) ;
2020-10-06 21:59:33 +00:00
return ;
}
2020-10-06 22:34:11 +00:00
int angle = ( plr - > angle + 180 ) % 360 ;
NPCManager : : updateNPCPosition ( npc - > appearanceData . iNPC_ID , npc - > appearanceData . iX , npc - > appearanceData . iY , npc - > appearanceData . iZ , angle ) ;
TableData : : RunningNPCRotations [ npc - > appearanceData . iNPC_ID ] = angle ;
// update rotation clientside
INITSTRUCT ( sP_FE2CL_NPC_ENTER , pkt ) ;
pkt . NPCAppearanceData = npc - > appearanceData ;
sock - > sendPacket ( ( void * ) & pkt , P_FE2CL_NPC_ENTER , sizeof ( sP_FE2CL_NPC_ENTER ) ) ;
2020-10-06 21:59:33 +00:00
2020-10-06 22:34:11 +00:00
ChatManager : : sendServerMessage ( sock , " [NPCR] Successfully set angle to " + std : : to_string ( angle ) + " for NPC " + std : : to_string ( npc - > appearanceData . iNPC_ID ) ) ;
2020-10-06 21:59:33 +00:00
}
2020-10-06 22:00:17 +00:00
void refreshCommand ( std : : string full , std : : vector < std : : string > & args , CNSocket * sock ) {
Player * plr = PlayerManager : : getPlayer ( sock ) ;
PlayerManager : : sendPlayerTo ( sock , plr - > x , plr - > y , plr - > z ) ;
}
2020-10-06 19:53:21 +00:00
void flushCommand ( std : : string full , std : : vector < std : : string > & args , CNSocket * sock ) {
ChatManager : : sendServerMessage ( sock , " Wrote gruntwork to " + settings : : GRUNTWORKJSON ) ;
TableData : : flush ( ) ;
}
2020-08-18 20:42:30 +00:00
void ChatManager : : init ( ) {
REGISTER_SHARD_PACKET ( P_CL2FE_REQ_SEND_FREECHAT_MESSAGE , chatHandler ) ;
REGISTER_SHARD_PACKET ( P_CL2FE_REQ_PC_AVATAR_EMOTES_CHAT , emoteHandler ) ;
2020-08-22 18:11:47 +00:00
REGISTER_SHARD_PACKET ( P_CL2FE_REQ_SEND_MENUCHAT_MESSAGE , menuChatHandler ) ;
2020-10-02 23:50:47 +00:00
registerCommand ( " test " , 1 , testCommand ) ;
registerCommand ( " access " , 100 , accessCommand ) ;
// TODO: add help command
2020-10-05 00:33:30 +00:00
registerCommand ( " mss " , 30 , mssCommand ) ;
2020-10-06 21:59:33 +00:00
registerCommand ( " npcr " , 30 , npcRotateCommand ) ;
2020-10-06 19:53:21 +00:00
registerCommand ( " flush " , 30 , flushCommand ) ;
2020-10-05 00:33:30 +00:00
registerCommand ( " level " , 50 , levelCommand ) ;
registerCommand ( " population " , 100 , populationCommand ) ;
2020-10-06 22:00:17 +00:00
registerCommand ( " refresh " , 100 , refreshCommand ) ;
2020-10-02 23:50:47 +00:00
}
void ChatManager : : registerCommand ( std : : string cmd , int requiredLevel , CommandHandler handlr ) {
commands [ cmd ] = ChatCommand ( requiredLevel , handlr ) ;
2020-08-18 20:42:30 +00:00
}
void ChatManager : : chatHandler ( CNSocket * sock , CNPacketData * data ) {
2020-08-20 23:50:30 +00:00
if ( data - > size ! = sizeof ( sP_CL2FE_REQ_SEND_FREECHAT_MESSAGE ) )
return ; // malformed packet
2020-10-05 00:03:13 +00:00
2020-08-18 20:42:30 +00:00
sP_CL2FE_REQ_SEND_FREECHAT_MESSAGE * chat = ( sP_CL2FE_REQ_SEND_FREECHAT_MESSAGE * ) data - > buf ;
2020-10-02 23:50:47 +00:00
Player * plr = PlayerManager : : getPlayer ( sock ) ;
std : : string fullChat = U16toU8 ( chat - > szFreeChat ) ;
2020-10-03 00:20:59 +00:00
if ( fullChat . length ( ) > 1 & & fullChat [ 0 ] = = CMD_PREFIX ) { // PREFIX
runCmd ( fullChat , sock ) ;
2020-10-02 23:50:47 +00:00
return ;
}
2020-08-19 18:22:51 +00:00
2020-08-19 18:25:54 +00:00
// send to client
2020-08-23 00:26:18 +00:00
INITSTRUCT ( sP_FE2CL_REP_SEND_FREECHAT_MESSAGE_SUCC , resp ) ;
2020-08-22 23:31:09 +00:00
memcpy ( resp . szFreeChat , chat - > szFreeChat , sizeof ( chat - > szFreeChat ) ) ;
2020-10-02 23:50:47 +00:00
resp . iPC_ID = plr - > iID ;
2020-08-22 23:31:09 +00:00
resp . iEmoteCode = chat - > iEmoteCode ;
sock - > sendPacket ( ( void * ) & resp , P_FE2CL_REP_SEND_FREECHAT_MESSAGE_SUCC , sizeof ( sP_FE2CL_REP_SEND_FREECHAT_MESSAGE_SUCC ) ) ;
2020-08-19 18:22:51 +00:00
2020-08-19 18:25:54 +00:00
// send to visible players
2020-09-17 22:45:43 +00:00
PlayerManager : : sendToViewable ( sock , ( void * ) & resp , P_FE2CL_REP_SEND_FREECHAT_MESSAGE_SUCC , sizeof ( sP_FE2CL_REP_SEND_FREECHAT_MESSAGE_SUCC ) ) ;
2020-08-18 20:42:30 +00:00
}
2020-09-16 19:46:15 +00:00
2020-08-22 18:11:47 +00:00
void ChatManager : : menuChatHandler ( CNSocket * sock , CNPacketData * data ) {
if ( data - > size ! = sizeof ( sP_CL2FE_REQ_SEND_MENUCHAT_MESSAGE ) )
return ; // malformed packet
sP_CL2FE_REQ_SEND_MENUCHAT_MESSAGE * chat = ( sP_CL2FE_REQ_SEND_MENUCHAT_MESSAGE * ) data - > buf ;
// send to client
2020-08-23 00:26:18 +00:00
INITSTRUCT ( sP_FE2CL_REP_SEND_MENUCHAT_MESSAGE_SUCC , resp ) ;
2020-08-22 23:31:09 +00:00
memcpy ( resp . szFreeChat , chat - > szFreeChat , sizeof ( chat - > szFreeChat ) ) ;
2020-08-24 22:02:07 +00:00
resp . iPC_ID = PlayerManager : : players [ sock ] . plr - > iID ;
2020-08-22 23:31:09 +00:00
resp . iEmoteCode = chat - > iEmoteCode ;
sock - > sendPacket ( ( void * ) & resp , P_FE2CL_REP_SEND_MENUCHAT_MESSAGE_SUCC , sizeof ( sP_FE2CL_REP_SEND_MENUCHAT_MESSAGE_SUCC ) ) ;
2020-08-18 20:42:30 +00:00
2020-08-22 18:11:47 +00:00
// send to visible players
2020-09-17 22:45:43 +00:00
PlayerManager : : sendToViewable ( sock , ( void * ) & resp , P_FE2CL_REP_SEND_MENUCHAT_MESSAGE_SUCC , sizeof ( sP_FE2CL_REP_SEND_MENUCHAT_MESSAGE_SUCC ) ) ;
2020-08-22 18:11:47 +00:00
}
2020-09-16 19:46:15 +00:00
2020-08-18 20:42:30 +00:00
void ChatManager : : emoteHandler ( CNSocket * sock , CNPacketData * data ) {
2020-08-19 00:11:31 +00:00
if ( data - > size ! = sizeof ( sP_CL2FE_REQ_PC_AVATAR_EMOTES_CHAT ) )
return ; // ignore the malformed packet
2020-08-18 20:42:30 +00:00
// you can dance with friends!!!!!!!!
sP_CL2FE_REQ_PC_AVATAR_EMOTES_CHAT * emote = ( sP_CL2FE_REQ_PC_AVATAR_EMOTES_CHAT * ) data - > buf ;
2020-10-02 23:50:47 +00:00
Player * plr = PlayerManager : : getPlayer ( sock ) ;
2020-09-14 13:53:48 +00:00
2020-08-18 20:42:30 +00:00
// send to client
2020-08-23 00:26:18 +00:00
INITSTRUCT ( sP_FE2CL_REP_PC_AVATAR_EMOTES_CHAT , resp ) ;
2020-08-22 23:31:09 +00:00
resp . iEmoteCode = emote - > iEmoteCode ;
2020-10-02 23:50:47 +00:00
resp . iID_From = plr - > iID ;
2020-08-22 23:31:09 +00:00
sock - > sendPacket ( ( void * ) & resp , P_FE2CL_REP_PC_AVATAR_EMOTES_CHAT , sizeof ( sP_FE2CL_REP_PC_AVATAR_EMOTES_CHAT ) ) ;
2020-08-18 20:42:30 +00:00
// send to visible players (players within render distance)
2020-09-17 22:45:43 +00:00
PlayerManager : : sendToViewable ( sock , ( void * ) & resp , P_FE2CL_REP_PC_AVATAR_EMOTES_CHAT , sizeof ( sP_FE2CL_REP_PC_AVATAR_EMOTES_CHAT ) ) ;
2020-08-18 20:42:30 +00:00
}
2020-10-02 23:50:47 +00:00
void ChatManager : : sendServerMessage ( CNSocket * sock , std : : string msg ) {
INITSTRUCT ( sP_FE2CL_PC_MOTD_LOGIN , motd ) ;
motd . iType = 1 ;
2020-10-04 17:52:16 +00:00
// convert string to u16 and write it to the buffer
2020-10-04 17:50:58 +00:00
U8toU16 ( msg , ( char16_t * ) motd . szSystemMsg , sizeof ( motd . szSystemMsg ) ) ;
2020-10-02 23:50:47 +00:00
// send the packet :)
sock - > sendPacket ( ( void * ) & motd , P_FE2CL_PC_MOTD_LOGIN , sizeof ( sP_FE2CL_PC_MOTD_LOGIN ) ) ;
}