commit c7faeb0441cf0b22c4368ab7a6d6f33f767bb17f Author: Wirlaburla Date: Tue Oct 3 13:02:07 2023 -0500 pengo time diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..c172c68 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 3.5) +project(pengobot) +SET(CMAKE_CXX_STANDARD 17) +SET(CMAKE_CXX_STANDARD_REQUIRED True) +SET(CMAKE_CXX_FLAGS "-O3") +add_subdirectory(src) +install(TARGETS pengobot RUNTIME DESTINATION bin) diff --git a/conf.examples/attentions.messages b/conf.examples/attentions.messages new file mode 100644 index 0000000..b32d74d --- /dev/null +++ b/conf.examples/attentions.messages @@ -0,0 +1,7 @@ +hey pengo +hey p3ng0 +p3ng0 +yo pengo +yo p3ng0 +hello p3ng0 +aloha p3ng0 diff --git a/conf.examples/goodbyes.messages b/conf.examples/goodbyes.messages new file mode 100644 index 0000000..b04c55e --- /dev/null +++ b/conf.examples/goodbyes.messages @@ -0,0 +1 @@ +Goodbye! diff --git a/conf.examples/greets.messages b/conf.examples/greets.messages new file mode 100644 index 0000000..c4eae6e --- /dev/null +++ b/conf.examples/greets.messages @@ -0,0 +1,3 @@ +Hi, I'm P3NG0, your friendly Worlds bot. +My initialization is complete. +Beep boop! diff --git a/conf.examples/pengobot.conf b/conf.examples/pengobot.conf new file mode 100644 index 0000000..2116c63 --- /dev/null +++ b/conf.examples/pengobot.conf @@ -0,0 +1,44 @@ +# This is the configuration file for PengoBot. + +# The username you log in as. Account must be registered on worlds servers. +username=Clem + +# The password to the username above. +password=hackme + +# The username who owns the bot. This is for administrative actions. +owner=Thom + +# The URL to whisper users when asking for bot help. +help_url= + +# Bot avatar. Must be VIP for articulated to show up. +avatar=http://files.worlio.com/users/wirlaburla/avatars/pengobot.mov + +# The position in the world that the bot will sit. +xpos=0 +ypos=0 +zpos=0 +direction=0 + +# Every 'keep alive', will turn x degrees. +spin=0 + +# Keep alive interval. Setting this too long will make worlds disconnect you. +katime=5 + +# The time between random messages. The wait period is between minRandomMsgTime and maxRandomMsgTime. Setting these to 0 will disable. +msg_random=conf/randoms.messages +minRandomMsgTime=300 +maxRandomMsgTime=900 + +# Local database of worlds and their names. +msg_whereis=conf/whereis.messages +worldfile=conf/worlds.conf + +# Other responses. +msg_attention=conf/attentions.messages +msg_greet=conf/greets.messages +msg_goodbye=conf/goodbyes.messages +msg_unknowncmd=conf/unknowncmd.messages +msg_whoami=conf/whoami.messages diff --git a/conf.examples/randoms.messages b/conf.examples/randoms.messages new file mode 100644 index 0000000..5631c41 --- /dev/null +++ b/conf.examples/randoms.messages @@ -0,0 +1,2 @@ +kiwi +mongolia diff --git a/conf.examples/unknowncmd.messages b/conf.examples/unknowncmd.messages new file mode 100644 index 0000000..ef27f59 --- /dev/null +++ b/conf.examples/unknowncmd.messages @@ -0,0 +1,4 @@ +Sorry, I don't know what you're asking. +I am unsure of your request. +ERROR: DIVISION BY ZERO +Need something? diff --git a/conf.examples/whereis.messages b/conf.examples/whereis.messages new file mode 100644 index 0000000..37eeee9 --- /dev/null +++ b/conf.examples/whereis.messages @@ -0,0 +1,4 @@ +It's right here: %s +%s +Here is your mark: %s +Found it! %s diff --git a/conf.examples/whoami.messages b/conf.examples/whoami.messages new file mode 100644 index 0000000..59c95fe --- /dev/null +++ b/conf.examples/whoami.messages @@ -0,0 +1,2 @@ +Hi, I'm P3NG0. I'm a friendly bot. +I am here to annihilate the human race. diff --git a/conf.examples/worlds.conf b/conf.examples/worlds.conf new file mode 100644 index 0000000..002fa8d --- /dev/null +++ b/conf.examples/worlds.conf @@ -0,0 +1,8 @@ +beach=http://ittraining.net/jimbly/groundzero.world +mugshots=http://ittraining.net/jimbly/mugshots.world +tyler=http://worlio.com/users/dsparil/Tyler%20World/tyler.world +party cave=http://www.ittraining.net/waterworld/waterworld.world +texas=http://ittraining.net/jimbly/texas01.world +cybergz=http://files.worlio.com/users/dosfox/sybergz/cybergz_cur.world +alien mystery=http://files.worlio.com/users/bilbo99/AlienMystery/alien.world +decennary=http://files.worlio.com/users/sl0nderman/worlds/decennary.world diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..2159e60 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,4 @@ +add_executable(pengobot + main.cpp +) +target_link_libraries(pengobot) diff --git a/src/client.h b/src/client.h new file mode 100644 index 0000000..9308222 --- /dev/null +++ b/src/client.h @@ -0,0 +1,78 @@ +#ifndef H_CLIENT +#define H_CLIENT +#include +#include +#include "drone.h" +#include "config.h" +PengoConfig conf; +bool debug = false; + +#define BUFFERSIZE 65535 + +bool autoOnline = false; +bool roomOnline = false; + +unsigned char bufout[BUFFERSIZE] = {0}; +int randWait; + +int autosock; +int roomsock; + +std::thread aRecv_t; +std::thread rRecv_t; +std::thread rKeepAlive_t; +std::thread rAutoMsg_t; + +uint8_t autoserver[4] = { 209, 240, 84, 122 }; +uint16_t autoport = 6650; +uint8_t roomserver[4] = { 209, 240, 84, 122 }; +uint16_t roomport = 5672; + +std::string login_username; +std::string login_password; +std::string avatar = "avatar:pengo.mov"; + +// Needs to include dimension too +std::string room; +uint16_t roomID = 1; + +int protocol = 24; +char* version = "1000000000"; +int avatars = 253; +int keepAliveTime; + +uint16_t xPos = 0; +uint16_t yPos = 0; +uint16_t zPos = 0; +uint16_t direction = 0; +uint16_t spin = 0; + +std::map properties; +std::map objects; + +int deinit(int response); +static char* trim(char *str); +char* zero(int size); +unsigned char* uzero(int size); +void autoInit(); +void roomInit(); +void roomKeepAlive(); +void autoRandMessage(); +void reciever(int *sock, uint16_t port); +void sessInit(int *sock, std::string username, std::string password); +void sessExit(int *sock); +void readPropertyList(unsigned char* in); +std::map readOldPropertyList(unsigned char* in); +void setAvatar(int *sock, std::string avatar); +void roomIDReq(int *sock, std::string room); +void teleport(int *sock, int x, int y, int z, int rot); +char* dimAdd(std::string room); +bool strcontains(std::string needle, std::string haystack); +bool vstrcontains(std::string needle, std::vector haystack); +void processText(int *sock, std::string username, std::string message); +void processWhisper(int *sock, std::string username, std::string message); +void sendChatMessage(int *sock, std::string msg); +void sendWhisperMessage(int *sock, std::string to, std::string msg); +int wsend(int *sock, unsigned char str[], int flags); + +#endif \ No newline at end of file diff --git a/src/cmds.h b/src/cmds.h new file mode 100644 index 0000000..9e23bf7 --- /dev/null +++ b/src/cmds.h @@ -0,0 +1,34 @@ +#ifndef H_CMDS +#define H_CMDS + +#define CMD_LONGLOC 1 +#define CMD_STATE 2 +#define CMD_PROP 3 +#define CMD_SHORTLOC 4 +#define CMD_ROOMCHANGE 5 +#define CMD_SESSINIT 6 +#define CMD_SESSEXIT 7 +#define CMD_APPINIT 9 +#define CMD_PROPREQ 10 +#define CMD_ACTOR_DISAPPR 11 +#define CMD_ACTOR_APPR 12 +#define CMD_REGOBJID 13 +#define CMD_CHATMSG 14 +#define CMD_PROPSET 15 +#define CMD_PROPUPD 16 +#define CMD_WHISPER 17 +#define CMD_TELEPORT 18 +#define CMD_ROOMIDREQ 20 +#define CMD_ROOMID 21 +#define CMD_SUBSCRIBE 22 +#define CMD_UNSUBSCRIBE 23 +#define CMD_SUBDIST 24 +#define CMD_REDIRECT 25 +#define CMD_REDIRID 26 +#define CMD_FINGREQ 27 +#define CMD_FINGREP 28 +#define CMD_BUDDYUPD 29 +#define CMD_BUDDYNTF 30 +#define CMD_CHANNEL 31 + +#endif diff --git a/src/config.h b/src/config.h new file mode 100644 index 0000000..0ce86f5 --- /dev/null +++ b/src/config.h @@ -0,0 +1,96 @@ +#ifndef CONFIG_H +#define CONFIG_H +#include +#include +#include + +class PengoConfig { +public: + PengoConfig() { + std::ifstream mconf("conf/pengobot.conf"); + + std::string line; + while (std::getline(mconf, line)) { + // Check if line is a comment line. + if (line.rfind("#", 0) != 0) { + std::string name = line.substr(0, line.find("=")); + std::string value = line.substr(line.find("=")+1, line.length()); + std::cout << name << ": \"" << value << "\"" << std::endl; + conf[name] = value; + } + } + + for (auto const& mc : conf) { + std::string msgName; + if ((mc.first.substr(0, mc.first.find("_"))) == "msg") { + msgName = mc.first.substr(mc.first.find("_")+1, mc.first.length()); + std::ifstream msglines(mc.second); + std::string line; + std::vector lines; + while (std::getline(msglines, line)) { + if (line.rfind("#", 0) != 0) { + lines.push_back(line); + std::cout << msgName << "=" << line << std::endl; + } + } + messages[msgName] = lines; + } + } + + if ((conf["worldfile"]).length() > 0) { + std::ifstream wrlds(conf["worldfile"]); + std::string wline; + while (std::getline(wrlds, wline)) { + if (wline.rfind("#", 0) != 0) { + std::string name = wline.substr(0, wline.find("=")); + std::string value = wline.substr(wline.find("=")+1, wline.length()); + std::cout << name << ": \"" << value << "\"" << std::endl; + worlds[name] = value; + } + } + } + } + PengoConfig operator = (PengoConfig *pc) { return *pc; }; + + std::string getValue(std::string name, std::string def) { + if (((std::string)conf[name]).length() > 0) return conf[name]; + else return def; + } + + int getInt(std::string name, int def) { + if (((std::string)conf[name]).length() > 0) return std::stoi(conf[name]); + else return def; + } + + void setValue(std::string name, std::string value) { + conf[name] = value; + } + + void setInt(std::string name, int value) { + conf[name] = std::to_string(value); + } + + std::string getMessage(std::string type) { + return (messages[type])[(int)(rand() % ((messages[type]).size()))]; + } + + std::vector getMessages(std::string type) { + return messages[type]; + } + + std::string getWorld(std::string name) { + return worlds[name]; + } + + std::map getWorlds() { + return worlds; + } + +private: + std::map conf; + std::map worlds; + std::map> messages; +}; + +#endif + diff --git a/src/drone.h b/src/drone.h new file mode 100644 index 0000000..c24d6ca --- /dev/null +++ b/src/drone.h @@ -0,0 +1,15 @@ +#ifndef H_DRONE +#define H_DRONE + +class Drone { +public: + bool droneActive = false; + char* name = new char[24]; + + Drone() { }; + Drone(char* &_name) : name{ _name } { }; + bool operator != (Drone &d) { return strcmp(this->name, d.name) != 0; }; + Drone operator = (Drone *d) { return *d; }; +}; + +#endif \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..c580ce5 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,623 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "strutils.h" +#include "client.h" +#include "verrors.h.h" +#include "cmds.h" +#include "props.h" + +#include + +static char* toLower(char* str) { + for(int i = 0; str[i]; i++){ + str[i] = std::tolower(str[i]); + } + return str; +} + +static std::string toLower(std::string str) { + std::transform(str.begin(), str.end(), str.begin(), + [](unsigned char c){ return std::tolower(c); }); + return str; +} + +static char* trim(char *str) { + char *end; + while(isspace(*str)) + str++; + if(*str == 0) + return str; + end = str + strnlen(str, 128) - 1; + while(end > str && isspace(*end)) + end--; + *(end+1) = '\0'; + return str; +} + +int main(int argc, char const* argv[]) { + srand (time(NULL)); + // Setting config values. + login_username = (conf.getValue("username", "")); + login_password = (conf.getValue("password", "")); + room = (conf.getValue("room", "GroundZero#Reception")); + avatar = (conf.getValue("avatar", "avatar:pengo.mov")); + xPos = conf.getInt("xpos", 0); + yPos = conf.getInt("ypos", 0); + zPos = conf.getInt("zpos", 0); + direction = conf.getInt("direction", 0); + spin = conf.getInt("spin", 45); + keepAliveTime = conf.getInt("katime", 15); + debug = conf.getInt("debug", 0) == 1; + autoInit(); + while (autoOnline) { + while (!roomOnline) {} + sleep(1); + } + return deinit(0); +} + +int deinit(int response) { + sendChatMessage(&roomsock, conf.getMessage("goodbye")); + if (roomOnline) sessExit(&roomsock); + roomOnline = false; + if (autoOnline) sessExit(&autosock); + autoOnline = false; + if (aRecv_t.joinable()) aRecv_t.detach(); + if (rRecv_t.joinable()) rRecv_t.detach(); + if (rKeepAlive_t.joinable()) rKeepAlive_t.detach(); + close(autosock); + close(roomsock); + return response; +} + +void autoInit() { + sockaddr_in auto_addr; + auto_addr.sin_family = AF_INET; + memcpy((void*)&auto_addr.sin_addr.s_addr, (void*)&autoserver, sizeof(roomserver)); + auto_addr.sin_port = htons(autoport); + + if ((autosock = socket(AF_INET, SOCK_STREAM, 0)) < 0) + perror("AutoSock creation error"); + + if (connect(autosock, (struct sockaddr*)&auto_addr, sizeof(auto_addr)) < 0) + perror("AutoServ connection failed"); + + aRecv_t = std::thread(reciever, &autosock, autoport); + wsend(&autosock, new unsigned char[] {0x03, 0xff, CMD_PROPREQ}, 0); + printf("info: Connected to AutoServer: %i.%i.%i.%i:%i\n", autoserver[1], autoserver[1], autoserver[2], autoserver[3], autoport); + sessInit(&autosock, login_username, login_password); + autoOnline = true; +} + +void roomInit() { + sockaddr_in room_addr; + room_addr.sin_family = AF_INET; + memcpy((void*)&room_addr.sin_addr.s_addr, (void*)&roomserver, sizeof(roomserver)); + room_addr.sin_port = htons(roomport); + + if ((roomsock = socket(AF_INET, SOCK_STREAM, 0)) < 0) + perror("AutoSock creation error"); + + if ((connect(roomsock, (struct sockaddr*)&room_addr, sizeof(room_addr))) < 0) + perror("RoomServ connection failed"); + + wsend(&autosock, new unsigned char[] {0x03, 0xff, CMD_PROPREQ}, 0); + printf("info: Connected to RoomServer: %i.%i.%i.%i:%i\n", roomserver[1], roomserver[1], roomserver[2], roomserver[3], roomport); + roomOnline = true; + rRecv_t = std::thread(reciever, &roomsock, roomport); + sessInit(&roomsock, login_username, login_password); + sleep(1); + rKeepAlive_t = std::thread(roomKeepAlive); + rAutoMsg_t = std::thread(autoRandMessage); +} + +void roomKeepAlive() { + while (roomOnline) { + if (direction >= 360) direction = 0; + else direction+=spin; + teleport(&roomsock, xPos, yPos, zPos, direction); + sleep(keepAliveTime); // So we don't auto-disconnect. + } + printf("warning: room keep alive disconnected!\n"); +} + +void autoRandMessage() { + int minTime = conf.getInt("minRandomMsgTime", 0); + int maxTime = conf.getInt("maxRandomMsgTime", 0); + int wait = 0; + sleep(2); + sendChatMessage(&roomsock, conf.getMessage("greet")); + if (minTime != 0) { + while (roomOnline) { + if (wait != 0) { + std::string newMsg = conf.getMessage("random"); + if (debug) printf("debug: automsg: \"%s\"\n", newMsg.c_str()); + sendChatMessage(&roomsock, newMsg); + } + wait = rand() % minTime + maxTime; + if (debug) printf("debug: waiting %i seconds for next message.\n", wait); + sleep(wait); + } + } + printf("warning: room auto-messenger disconnected! Ignore if minRandomMsgTime is 0.\n"); +} + +void reciever(int *sock, uint16_t port) { + unsigned char bufin[BUFFERSIZE] = {}; + while (read(*sock, bufin, sizeof(bufin)) > 0) { + int p = 0; + more_in_buffer: + int buflen = bufin[p]; + if (buflen == 0x00) continue; + memset(&(bufout[0]), 0, sizeof(bufout)); + if (bufin[p+2] == CMD_PROPUPD && bufin[p+1] == 0xff) { + readPropertyList(bufin); + } else if (bufin[p+2] == CMD_CHATMSG && bufin[p+1] == 0x01) { + char *username = new char[32]; + char *message = new char[250]; + int offs = p+3; + offs++; // Ignore empty byte + int username_len = bufin[offs++]; + memcpy(username, &bufin[offs], username_len); + username[username_len+1] = 0; + offs+=username_len; + int message_len = bufin[offs++]; + memcpy(message, &bufin[offs++], message_len); + message[message_len+1] = 0; + printf("info: received message from %s: \"%s\"\n", username, message); + processText(sock, username, message); + } else if (bufin[p+2] == CMD_WHISPER && bufin[p+1] == 0x01) { + char *username = new char[32]; + char *message; + int offs = p+3; + offs++; // Ignore empty byte + int username_len = bufin[offs++]; + memcpy(username, &bufin[offs], username_len); + username[username_len] = 0; + offs+=username_len; + message = new char[250-username_len]; + int message_len = bufin[offs++]; + memcpy(message, &bufin[offs++], message_len); + message[message_len] = 0; + printf("info: received whisper from %s: \"%s\"\n", username, message); + processWhisper(sock, username, message); + } else if (bufin[p+2] == CMD_REGOBJID && bufin[p+1] == 0xff) { + char *longID = new char[32]; + int shortID; + int offs = p+3; + int long_len = bufin[offs++]; + memcpy(longID, &bufin[offs], long_len); + longID[long_len] = 0; + offs+=long_len; + shortID = bufin[offs++]; + if (longID != NULL || strlen(longID) > 0) { + Drone newDrone = *(new Drone(longID)); + objects[shortID] = newDrone; + } + } else if (bufin[p+2] == CMD_SESSEXIT && bufin[p+1] == 0x01) { + autoOnline = roomOnline = false; + deinit(0); + } else if (bufin[p+2] == CMD_SESSINIT && bufin[p+1] == 0x01) { + std::map props = readOldPropertyList(&bufin[p]); + int errNo = VAR_OK; + for (auto const& p : props) + switch (p.first) { + case PROP_ERROR: + errNo = atoi(p.second); + break; + } + if (errNo != VAR_OK) { + char* errMsg = "UNKNOWN"; + switch(errNo) { + case VAR_BAD_PASSWORD: + errMsg = "Invalid Password."; break; + case VAR_BAD_ACCOUNT: + errMsg = "Account is no longer valid."; break; + case VAR_BAD_IPADDRESS: + errMsg = "Invalid client IP address!"; break; + case VAR_NO_SUCH_USER: + errMsg = "User does not exist."; break; + } + printf("error: code %i received: %s\n", errNo, errMsg); + + if (port == autoport) { + autoOnline = false; + } else { + roomOnline = false; + } + } else { + if (port != autoport) { + setAvatar(sock, avatar); + } else { + roomIDReq(sock, room); + } + } + } else if ((bufin[p+2] == CMD_ROOMID || bufin[p+2] == 0x1A) && bufin[p+1] == 0x01) { + char *longID = new char[255]; + int shortID; + int offs = p+3; + int long_len = bufin[offs++]; + memcpy(longID, &bufin[offs], long_len); + longID[long_len+1] = 0; + offs+=long_len; + shortID = ((uint16_t)bufin[offs++] << 8) | bufin[offs++]; + roomID = shortID; + memcpy(&roomserver, new uint8_t[4]{ bufin[offs++], bufin[offs++], bufin[offs++], bufin[offs++] }, 4); + roomport = (bufin[offs++] << 8) | bufin[offs++]; + if (port != autoport) sessExit(sock); + printf("info: Joining room %s\n", longID); + objects.erase(objects.begin(), objects.end()); + roomInit(); + teleport(sock, xPos, yPos, zPos, direction); + } else if (bufin[p+2] == CMD_ACTOR_DISAPPR && bufin[p+1] == 0xfe) { + for (int t = 3; t < buflen; t++) { + userExit(bufin[p+t]); + } + } else if (bufin[p+2] == CMD_ACTOR_APPR && bufin[p+1] == 0xfe) { + for (int t = 3; t < buflen; t+=11) { + userEnter(bufin[p+t]); + } + } else if (bufin[p+2] == CMD_TELEPORT && bufin[p+1] == 0xfe) { + int offs = p+3; + int objID = bufin[offs++]; + int exitType = bufin[offs++]; + int entryType = bufin[offs++]; + int roomid = ((uint16_t)bufin[offs++] << 8) | bufin[offs++]; + if (exitType != 0) { + // De-register ObjID and say user left. + userExit(objID); + } else { + if (entryType == 0) { + userExit(objID); + } else { + userEnter(objID); + } + } + } else { + //printf("*LEN[%i] OID[%i] TYPE[%02X] OFF[%i]\n", bufin[p], bufin[p+1], bufin[p+2], p); + } + if (buflen < sizeof(bufin) && bufin[bufin[p]] != 0x00) { + p += bufin[p]; + goto more_in_buffer; + } + memset(&(bufin[0]), 0, sizeof(bufin)); + } +} + +void sessInit(int *sock, std::string username, std::string password) { + bufout[1] = 0x01; + bufout[2] = CMD_SESSINIT; + int l = 3; + + // Username + bufout[l++] = 2; + bufout[l++] = username.length(); + for (int c = 0; c < username.length(); c++) + bufout[l++] = username[c]; + + // Password + bufout[l++] = 6; + bufout[l++] = password.length(); + for (int c = 0; c < password.length(); c++) + bufout[l++] = password[c]; + + // Protocol + char* pstr = new char[4]; + sprintf(pstr, "%d", protocol); + bufout[l++] = 3; + bufout[l++] = strlen(pstr); + for (int c = 0; c < strlen(pstr); c++) + bufout[l++] = pstr[c]; + + // Avatars + char* avstr = new char[4]; + sprintf(avstr, "%d", avatars); + bufout[l++] = 7; + bufout[l++] = strlen(avstr); + for (int c = 0; c < strlen(avstr); c++) + bufout[l++] = avstr[c]; + + // Version + bufout[l++] = 9; + bufout[l++] = strlen(version); + for (int c = 0; c < strlen(version); c++) + bufout[l++] = version[c]; + + // Version + bufout[l++] = 12; + bufout[l++] = 1; + bufout[l++] = '1'; + + bufout[0] = l; + bufout[l+1] = 0; + wsend(sock, bufout, 0); +} + +void sessExit(int *sock) { + bufout[0] = 0x03; + bufout[1] = 0x01; + bufout[2] = CMD_SESSEXIT; + wsend(sock, bufout, 0); +} + +void constructPropertyList(int type, std::map props, unsigned char* snd) { + snd[1] = 0x01; + snd[2] = type; + int l = 3; + for (auto const& p : props) { + snd[l++] = p.first; + snd[l++] = strlen(p.second); + for (int c = 0; c < strlen(p.second); c++) + snd[l++] = p.second[c]; + } + snd[0] = l; + snd[l+1] = 0; +} + +void readPropertyList(unsigned char* in) { + int l {3}; + while (l < in[0]) { + char property[128] = {0}; + int type = in[l++]; + l++; l++; + int len = in[l++]; + memcpy(property, &in[l], len); + property[len]='\0'; + properties[type] = (char*)property; + l+=len; + } +} + +std::map readOldPropertyList(unsigned char* in) { + std::map oldprops = {}; + int l {3}; + while (l < in[0]) { + char* value = new char[255]; + char type = in[l++]; + int len = in[l++]; + memcpy(value, &in[l], len); + l+=len; + value[len]=0; + oldprops[type] = value; + } + return oldprops; +} + +void setAvatar(int *sock, std::string avstr) { + unsigned char bufav[255] = {0}; + int l = 1; + bufav[l++] = 0x01; + bufav[l++] = CMD_PROPSET; + bufav[l++] = 0x00; + bufav[l++] = 0x05; + bufav[l++] = 0x40; + bufav[l++] = 0x01; + + bufav[l++] = avstr.length(); + for (int c = 0; c < avstr.length(); c++) + bufav[l++] = avstr[c]; + + bufav[0] = l; + bufav[l+1] = 0; + wsend(sock, bufav, 0); +} + +void roomIDReq(int *sock, std::string room) { + unsigned char bufrm[255] = {0}; + bufrm[1] = 1; + bufrm[2] = CMD_ROOMIDREQ; + bufrm[3] = room.length(); + int x = 4; + for (int z = 0; z < room.length(); z++) + bufrm[x++] = room[z]; + bufrm[x++] = 0; + bufrm[0] = x; + wsend(sock, bufrm, 0); +} + +void teleport(int *sock, int x, int y, int z, int rot) { + unsigned char buftp[16] = {0}; + uint8_t _roomID[2]; + uint8_t _x[2]; + uint8_t _y[2]; + uint8_t _z[2]; + uint8_t _rot[2]; + memcpy(_roomID, &roomID, sizeof(_roomID)); + memcpy(_x, &x, sizeof(_x)); + memcpy(_y, &y, sizeof(_y)); + memcpy(_z, &z, sizeof(_z)); + memcpy(_rot, &rot, sizeof(_rot)); + + buftp[0] = 0x0f; + buftp[1] = 0x01; + buftp[2] = CMD_TELEPORT; + buftp[4] = _roomID[0]; buftp[3] = _roomID[1]; + buftp[5] = 0x00; buftp[6] = 0x01; + buftp[8] = _x[0]; buftp[7] = _x[1]; + buftp[10] = _y[0]; buftp[9] = _y[1]; + buftp[12] = _z[0]; buftp[11] = _z[1]; + buftp[14] = _rot[0]; buftp[13] = _rot[1]; + + wsend(sock, buftp, 0); +} + +void userEnter(char id) { + if (!((Drone)objects[id]).droneActive) { + objects[id].droneActive = true; + } +} + +void userExit(char id) { + if (((Drone)objects[id]).droneActive) { + objects.erase((char)id); + } +} + + +bool strcontains(std::string needle, std::string haystack) { + bool found = haystack.find(needle) != std::string::npos; + if (debug) printf("debug: %s =?= %s == %i\n", needle.c_str(), haystack.c_str(), found?1:0); + return found; +} + +bool vstrcontains(std::string needle, std::vector haystack) { + for (std::string str : haystack) { + if (needle.rfind(str, 0) == 0) return true; + } + return false; +} + +std::string getContainedWorld(std::map worldlist, std::string input) { + for (auto world : worldlist) { + if (strcontains(world.first, input)) return world.second; + } + return ""; +} + +char* handleCommand(std::string from, std::string message) { + char *msgout = new char[255]; + if (strcontains("flip a coin", message)) { + + bool heads = rand() % 10 > 5; + snprintf(msgout, 255, "%s", heads?"Heads":"Tails"); + + } else if (strcontains("time", message)) { + + time_t currentTime; + struct tm *localTime; + time( ¤tTime ); + localTime = localtime( ¤tTime ); + snprintf(msgout, 255, "The time is %02i:%02i.", localTime->tm_hour, localTime->tm_min, localTime->tm_sec); + + } else if (strcontains("dice", message) || strcontains("roll", message)) { + + int dice = 6; + + if (strcontains("d10", message)) dice = 10; + else if (strcontains("d12", message)) dice = 12; + else if (strcontains("d20", message)) dice = 20; + else if (strcontains("d100", message)) dice = 100; + + int roll = (rand() % (dice-1)) + 1; + snprintf(msgout, 255, "%s rolled a %i.", from.c_str(), dice); + + } else if (strcontains("where is", message) || strcontains("where", message) || strcontains("mark to", message) || strcontains("show me", message)) { + + std::string mark = getContainedWorld(conf.getWorlds(), message); + if (mark.length() > 0) { + snprintf(msgout, 255, conf.getMessage("whereis").c_str(), mark.c_str()); + } else { + snprintf(msgout, 255, "Sorry! Not a clue."); + } + + } else if (strcontains("many users", message) || strcontains("whos online", message) || strcontains("who is online", message) || strcontains("many online", message)) { + + snprintf(msgout, 255, "There are %i users in this room.", objects.size()); + + } else if ((strcontains("what", message) || strcontains("who", message)) && strcontains("are you", message)) { + + snprintf(msgout, 255, "%s", conf.getMessage("whoami").c_str()); + + } else if (strcontains("shutdown", message) && from == conf.getValue("owner", "")) { + + exit(deinit(0)); + + } + return msgout; +} + +void processText(int *sock, std::string username, std::string message) { + char *msgout = new char[255]; + message = toLower(message); // Make it a lowercase string so we can work with it. + // Someone has requested P3NG0s attention. + // We'll accept some variations. + if (username != login_username) { + if (vstrcontains(message, conf.getMessages("attention"))) { + msgout = handleCommand(username, message); + if (strlen(msgout) > 0) sendChatMessage(sock, msgout); + else if (strcontains("your commands", message) || strcontains("can you do", message)) { + + char *whisout = new char[255]; + snprintf(whisout, 255, "Check this out: %s", conf.getValue("help_url", "").c_str()); + sendWhisperMessage(&roomsock, username, whisout); + snprintf(msgout, 255, "You have been whispered with a link that will help you."); + sendChatMessage(sock, msgout); + + } + } else if (message == "ping") { // We'll accept a simple ping to pong. + sendChatMessage(sock, "Pong!"); + } + } +} + +void processWhisper(int *sock, std::string username, std::string message) { + char *msgout = new char[255]; + message = toLower(message); + + if (message == "ping") { + sendWhisperMessage(sock, username, "Pong"); + } else { + msgout = handleCommand(username, message); + if (strlen(msgout) > 0) sendWhisperMessage(sock, username, msgout); + } +} + +void sendChatMessage(int *sock, std::string msg) { + unsigned char bufout[BUFFERSIZE] = {0}; + int msglen = msg.length(); + int k = 1; + bufout[k++] = 1; + bufout[k++] = CMD_CHATMSG; + bufout[k++] = 0; bufout[k++] = 0; + bufout[k++] = msglen; + for(int l = 0; l < msglen; l++) + bufout[k++] = msg[l]; + bufout[k] = 0; + bufout[0] = k; + wsend(&roomsock, bufout, 0); +} + +void sendWhisperMessage(int *sock, std::string to, std::string msg) { + whisper_repeat: + unsigned char bufout[BUFFERSIZE] = {0}; + int k = 1; + bufout[k++] = 0; + bufout[k++] = to.length(); + for(int l = 0; l < to.length(); l++) + bufout[k++] = to[l]; + bufout[k++] = CMD_WHISPER; + bufout[k++] = 0; bufout[k++] = 0; + int msglen = std::min((int)msg.length(), 226); + bufout[k++] = msglen; + for(int l = 0; l < msglen; l++) + bufout[k++] = msg[l]; + bufout[k] = 0; + bufout[0] = k; + wsend(&roomsock, bufout, 0); + // Check if the length is higher. + if (msg.length() > 226) { + msg = msg.substr(226, -1); + goto whisper_repeat; + } +} + +int wsend(int *sock, unsigned char str[], int flags) { + if (debug) printf("debug: sending new packet of type %i and length %i.\n", str[2], str[0]); + return send(*sock, str, str[0], flags); +} diff --git a/src/props.h b/src/props.h new file mode 100644 index 0000000..d835976 --- /dev/null +++ b/src/props.h @@ -0,0 +1,32 @@ +#ifndef PROPS_H +#define PROPS_H + +#define PROP_APPNAME 1 +#define PROP_USERNAME 2 +#define PROP_PROTOCOL 3 +#define PROP_ERROR 4 +#define PROP_CHANNEL 5 +#define PROP_PASSWORD 6 +#define PROP_AVATARS 7 +#define PROP_UPDATETIME 8 +#define PROP_CLIENT 9 +#define PROP_SERIAL 10 +#define PROP_EMAIL 11 +#define PROP_LOGONOFF 12 +#define PROP_DURATION 13 +#define PROP_GUEST 14 +#define PROP_SERVERTYPE 15 +#define PROP_BIZCARD 16 +#define PROP_NEW_PASSWORD 20 +#define PROP_PRIV 22 +#define PROP_ASLEEP 23 +#define PROP_EX_HTTP_SERVER 24 +#define PROP_SCRIPT_SERVER 25 +#define PROP_SMTP_SERVER 26 +#define PROP_MAIL_DOMAIN 27 +#define PROP_NEW_USERNAME 28 +#define PROP_IN_HTTP_SERVER 29 +#define PROP_INVENTORY 30 + +#endif + diff --git a/src/strutils.h b/src/strutils.h new file mode 100644 index 0000000..6a8742a --- /dev/null +++ b/src/strutils.h @@ -0,0 +1,25 @@ +#ifndef STRUTILS_H +#define STRUTILS_H + +#include +#include +#include +#include + +template +void split(const std::string &s, char delim, Out result) { + std::istringstream iss(s); + std::string item; + while (std::getline(iss, item, delim)) { + *result++ = item; + } +} + +std::vector split(const std::string &s, char delim) { + std::vector elems; + split(s, delim, std::back_inserter(elems)); + return elems; +} + +#endif + diff --git a/src/verrors.h.h b/src/verrors.h.h new file mode 100644 index 0000000..88dc925 --- /dev/null +++ b/src/verrors.h.h @@ -0,0 +1,29 @@ +#ifndef H_VAR_ERROR +#define H_VAR_ERROR + +#define VAR_OK 0 +#define VAR_BAD_USER 1 +#define VAR_MAX_ORDINARY 2 +#define VAR_MAX_PRIORITY 3 +#define VAR_FATAL 5 +#define VAR_BAD_PROTOCOL 6 +#define VAR_BAD_CLIENTSW 7 +#define VAR_BAD_SERIAL 9 +#define VAR_TAKEN_SERIAL 10 +#define VAR_TAKEN_USER 11 +#define VAR_NO_SUCH_USER 12 +#define VAR_BAD_PASSWORD 13 +#define VAR_BAD_ACCOUNT 14 +#define VAR_NOT_LOGGEDON 15 +#define VAR_BAD_IPADDRESS 16 +#define VAR_LOGGEDON 17 +#define VAR_ROOM_FULL 21 +#define VAR_UNEXPECTED 100 | 101 | 102 | 103 +#define VAR_UNREACHABLE 104 | 105 | 106 | 107 +#define VAR_SHUTTING_DOWN 201 +#define VAR_RECONNECTING 202 +#define VAR_DISCONNECTED 203 +#define VAR_LOST_CONNECTION 204 +#define VAR_SINGLE_USER 205 + +#endif \ No newline at end of file