#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "client.h" #include "var_error.h" #include "cmds.h" 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[]) { unsigned char bufout[BUFFERSIZE] = {0}; int status, valread, autosock, roomsock; struct sockaddr_in serv_addr; std::cout << "Username: "; std::cin >> login_username; initscr(); cbreak(); noecho(); refresh(); printf("*Password: "); std::cin >> login_password; wlog = newwin(LINES-1, COLS, 0, 0); wmove(wlog, LINES-2, 0); scrollok(wlog, true); wprintw(wlog, "\n*Connecting to Worlds.com. Enter '.help' for a list of commands."); wform = newwin(1, COLS, LINES-1, 0); wmove(wform, 0, 0); scrollok(wform, true); keypad(wform, TRUE); entries[0] = new_field(1, COLS, LINES-1, 0, 0, 0); set_field_buffer(entries[0], 0, ""); set_field_opts(entries[0], O_VISIBLE | O_PUBLIC | O_EDIT | O_ACTIVE); field_opts_off(entries[0], O_AUTOSKIP); form = new_form(entries); set_form_win(form, wform); post_form(form); set_current_field(form, entries[0]); if ((autosock = socket(AF_INET, SOCK_STREAM, 0)) < 0) perror("Socket creation error."); serv_addr.sin_family = AF_INET; inet_pton(AF_INET, SERVERADDR, &serv_addr.sin_addr); serv_addr.sin_port = htons(AUTOSERVER); if ((status = connect(autosock, (struct sockaddr*)&serv_addr, sizeof(serv_addr))) < 0) perror("AutoServ connection failed."); // Initialize connection. std::thread aRecv_t(reciever, autosock); autoInit(autosock); serv_addr.sin_port = htons(ROOMSERVER); close(autosock); if ((roomsock = socket(AF_INET, SOCK_STREAM, 0)) < 0) perror("Socket creation error."); if ((status = connect(roomsock, (struct sockaddr*)&serv_addr, sizeof(serv_addr))) < 0) perror("RoomServ connection failed"); std::thread rRecv_t(reciever, roomsock); std::thread rInit_t(roomInit, roomsock); int key = 0; while ((key = wgetch(wform))) { memset(&(bufout[0]), 0, sizeof(bufout)); if (key == 10) { form_driver(form, REQ_VALIDATION); char* inmsg = trim(field_buffer(entries[0], 0)); int msglen = 0; if ((msglen = strlen(inmsg)) > 0) { if (inmsg[0] == '.' && msglen > 1) { // Starting a command. std::ifstream srcFile; int srcLine = -1; int msgoff = 1; ccmd_parse: char cmd[16] = {0}; char s; int j = 0; while (msgoff < msglen && (s = inmsg[msgoff++]) != 0x20) { cmd[j++] = s; } cmd[j++] = 0; if (strcmp(cmd, "h") == 0 || strcmp(cmd, "help") == 0) { wprintw(wlog, "\n*Command help: "); wprintw(wlog, "\n* .h Show this"); wprintw(wlog, "\n* .help"); wprintw(wlog, "\n* .login Login as a user"); wprintw(wlog, "\n* .source Run commands from a text file"); wprintw(wlog, "\n* .f [user] Modify friends list"); wprintw(wlog, "\n* .friends [user]"); wprintw(wlog, "\n* .mute [user] Mute users"); wprintw(wlog, "\n* .w Whisper a message to a user"); wprintw(wlog, "\n* .whisper "); wprintw(wlog, "\n* .avatar Change avatar"); wprintw(wlog, "\n* .teleport [#] Teleport to coordinates"); wprintw(wlog, "\n* .dimension Change dimension"); wprintw(wlog, "\n* .list Show room users"); wprintw(wlog, "\n* .clear Clear chat log"); wprintw(wlog, "\n* .logout Logs out of current user"); wprintw(wlog, "\n* .q Quit"); } else if (strcmp(cmd, "source") == 0) { char file[32] = {0}; j = 0; while (msgoff < msglen && (s = inmsg[msgoff++]) != 0x20) { file[j++] = s; } file[j++] = 0; srcFile.open(file); if (srcFile.is_open()) { wprintw(wlog, "\n*Sourcing from file %s", file); inmsg = new char[256]; srcLine = 0; } } else if ((strcmp(cmd, "w") == 0 || strcmp(cmd, "whisper") == 0) && srcLine < 0) { int k = 1; bufout[k++] = 0; char username[32] = {0}; j = 0; while (msgoff < msglen && (s = inmsg[msgoff++]) != 0x20) { username[j++] = s; } username[j++] = 0; char message[255] = {0}; j = 0; while (msgoff < msglen && (s = inmsg[msgoff++]) != 0x00) { message[j++] = s; } message[j++] = 0; if (strlen(username) == 0 || strlen(message) == 0) { wprintw(wlog, "\n*.whisper "); continue; } bufout[k++] = strlen(username); for(int l = 0; l < strlen(username); l++) bufout[k++] = username[l]; bufout[k++] = CMD_WHISPER; bufout[k++] = 0; bufout[k++] = 0; bufout[k++] = strlen(message); for(int l = 0; l < strlen(message); l++) bufout[k++] = message[l]; bufout[k] = 0; bufout[0] = k; wprintw(wlog, "\n <- [%s] %s", username, message); wsend(roomsock, bufout, 0); } else if (strcmp(cmd, "f") == 0 || strcmp(cmd, "friends") == 0) { char subcmd[4] = {0}; j = 0; while (msgoff < msglen && (s = inmsg[msgoff++]) != 0x20) { subcmd[j++] = s; } subcmd[j++] = 0; if (strcmp(subcmd, "add") == 0) { char *username = new char[16]; j = 0; while (msgoff < msglen && (s = inmsg[msgoff++]) != 0x00) { username[j++] = s; } username[j] = 0; bool already_exists = false; for (std::vector::iterator v = friends.begin(); v != friends.end(); ++v) if (strcmp(username, *v) == 0) { already_exists = true; break; } if (!already_exists) { friends.push_back(username); int k = 0; bufout[k++] = j+5; bufout[k++] = 0x01; bufout[k++] = CMD_BUDDYUPD; bufout[k++] = j; for(int l = 0; l < j; l++) bufout[k++] = username[l]; bufout[k++] = 0x01; bufout[k] = 0; wprintw(wlog, "\n*%s added to friendslist.", username); wsend(roomsock, bufout, 0); } else { wprintw(wlog, "\n*%s is already your friend!", username); } } else if (strcmp(subcmd, "del") == 0) { char *username = new char[16]; j = 0; while (msgoff < msglen && (s = inmsg[msgoff++]) != 0x00) { username[j++] = s; } username[j] = 0; bool existed = false; for (std::vector::iterator v = friends.begin(); v != friends.end(); ++v) if (strcmp(username, *v) == 0) { existed = true; friends.erase(v); break; } if (existed) { int k = 0; bufout[k++] = j+5; bufout[k++] = 0x01; bufout[k++] = CMD_BUDDYUPD; bufout[k++] = j; for(int l = 0; l < j; l++) bufout[k++] = username[l]; bufout[k++] = 0x00; bufout[k] = 0; wprintw(wlog, "\n*%s removed from friendslist.", username); wsend(roomsock, bufout, 0); } else { wprintw(wlog, "\n*%s isn't on your friendslist.", username); } } else if (strcmp(subcmd, "list") == 0) { wprintw(wlog, "\n*Friendslist:"); for (std::vector::iterator v = friends.begin(); v != friends.end(); ++v) wprintw(wlog, "%s %s", v==friends.begin()?",":"", *v); } else { wprintw(wlog, "\n.friend [user]"); } } else if (strcmp(cmd, "mute") == 0) { char subcmd[4] = {0}; j = 0; while (msgoff < msglen && (s = inmsg[msgoff++]) != 0x20) { subcmd[j++] = s; } subcmd[j++] = 0; if (strcmp(subcmd, "add") == 0) { char *username = new char[16]; j = 0; while (msgoff < msglen && (s = inmsg[msgoff++]) != 0x00) { username[j++] = s; } username[j] = 0; bool already_exists = false; for (std::vector::iterator v = mutes.begin(); v != mutes.end(); ++v) if (strcmp(username, *v) == 0) { already_exists = true; break; } if (!already_exists) { mutes.push_back(username); wprintw(wlog, "\n*%s added to mutelist.", username); } else { wprintw(wlog, "\n*%s is already muted.", username); } } else if (strcmp(subcmd, "del") == 0) { char *username = new char[16]; j = 0; while (msgoff < msglen && (s = inmsg[msgoff++]) != 0x00) { username[j++] = s; } username[j] = 0; bool existed = false; for (std::vector::iterator v = mutes.begin(); v != mutes.end(); ++v) if (strcmp(username, *v) == 0) { existed = true; mutes.erase(v); break; } if (existed) { wprintw(wlog, "\n*%s removed from mutelist.", username); } else { wprintw(wlog, "\n*%s isn't muted.", username); } } else if (strcmp(subcmd, "list") == 0) { wprintw(wlog, "\n*Mutelist:"); for (std::vector::iterator v = mutes.begin(); v != mutes.end(); ++v) wprintw(wlog, "%s %s", v==mutes.begin()?",":"", *v); } else { wprintw(wlog, "\n.mute [user]"); } } else if (strcmp(cmd, "avatar") == 0) { char avatar[32] = {0}; int j = 0; while (msgoff < msglen && (s = inmsg[msgoff++]) != 0x20) { avatar[j++] = s; } avatar[j] = 0; if (strlen(avatar) == 0) { wprintw(wlog, "\n*.avatar "); continue; } wprintw(wlog, "\n*Avatar set to: %s", avatar); setAvatar(roomsock, avatar); } else if (strcmp(cmd, "teleport") == 0) { char _x[6] = {0}; int j = 0; while (msgoff < msglen && (s = inmsg[msgoff++]) != 0x20) { _x[j++] = s; } _x[j] = 0; xPos = atoi(_x); char _y[6] = {0}; j = 0; while (msgoff < msglen && (s = inmsg[msgoff++]) != 0x20) { _y[j++] = s; } _y[j] = 0; yPos = atoi(_y); char _z[6] = {0}; j = 0; while (msgoff < msglen && (s = inmsg[msgoff++]) != 0x20) { _z[j++] = s; } _z[j] = 0; zPos = atoi(_z); char _r[6] = {0}; j = 0; while (msgoff < msglen && (s = inmsg[msgoff++]) != 0x20) { _r[j++] = s; } _r[j] = 0; rot = atoi(_r); char _room[32] = {0}; j = 0; while (msgoff < msglen && (s = inmsg[msgoff++]) != 0x00) { _room[j++] = s; } _room[j] = 0; if (strlen(_room) == 0) { teleport(roomsock, xPos, yPos, zPos, rot); wprintw(wlog, "\n*Teleported to [%u,%u,%u,%u]", xPos, yPos, zPos, rot); } else { room = _room; wprintw(wlog, "\n*Teleporting to %s [%u,%u,%u,%u]", room, xPos, yPos, zPos, rot); roomIDReq(roomsock, room); } } else if (strcmp(cmd, "dimension") == 0) { char dim[32] = {0}; int j = 0; while (msgoff < msglen && (s = inmsg[msgoff++]) != 0x20) { dim[j++] = s; } dim[j] = 0; if (strlen(dim) == 0) { wprintw(wlog, "\n*.dimension "); continue; } wprintw(wlog, "\n*Changing dimension: %s", dim); dimension = dim; roomIDReq(roomsock, room); } else if (strcmp(cmd, "clear") == 0) { wclear(wlog); wmove(wlog, LINES-2, 0); } else if (strcmp(cmd, "list") == 0) { bool _f = false; wprintw(wlog, "\n*Users in room:"); for (const auto& [key, value] : objects) { wprintw(wlog, "%s %s", _f?",":"", value.name); _f=true; } } else if (strcmp(cmd, "q") == 0) { goto quit; } else { wprintw(wlog, "\n*Unknown command: \"%s\"", cmd); } if (srcLine >= 0 && srcFile.getline(inmsg, 255)) { srcLine++; wprintw(wlog, "\n#%s", inmsg); msgoff = 0; goto ccmd_parse; } else { srcLine = -1; srcFile.close(); } } else { 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++] = inmsg[l]; bufout[k] = 0; bufout[0] = k; wsend(roomsock, bufout, 0); } bufout[0] = 0; set_field_buffer(entries[0], 0, ""); } } else if (key == KEY_LEFT) { form_driver(form, REQ_PREV_CHAR); } else if (key == KEY_RIGHT) { form_driver(form, REQ_NEXT_CHAR); } else if (key == KEY_BACKSPACE || key == 127) { form_driver(form, REQ_DEL_PREV); } else if (key == KEY_DC) { form_driver(form, REQ_DEL_CHAR); } else if (key == KEY_HOME) { form_driver(form, REQ_BEG_LINE); } else if (key == KEY_END) { form_driver(form, REQ_END_LINE); } else { form_driver(form, key); } pos_form_cursor(form); wrefresh(wform); wrefresh(wlog); refresh(); } quit: unpost_form(form); free_form(form); free_field(entries[0]); endwin(); close(roomsock); return 0; } void autoInit(int sock_fd) { wsend(sock_fd, new unsigned char[] {0x03, 0xff, CMD_PROPREQ}, 0); sleep(1); sessInit(sock_fd, login_username, login_password); sleep(1); setAvatar(sock_fd, default_avatar); roomIDReq(sock_fd, room); pos_form_cursor(form); wrefresh(wform); wrefresh(wlog); refresh(); } void roomInit(int sock_fd) { wsend(sock_fd, new unsigned char[] {0x03, 0xff, CMD_PROPREQ}, 0); sleep(1); sessInit(sock_fd, login_username, login_password); setAvatar(sock_fd, default_avatar); while (true) { teleport(sock_fd, xPos, yPos, zPos, rot); sleep(15); } } void reciever(int sock_fd) { unsigned char bufin[BUFFERSIZE] = {}; unsigned char bufout[BUFFERSIZE] = {}; while (read(sock_fd, 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) { bool muted = false; char *username = new char[16]; 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; for (std::vector::iterator v = mutes.begin(); v != mutes.end(); ++v) if (strcmp(username, *v) == 0) { muted = true; break; } if (!muted) { offs+=username_len; int message_len = bufin[offs++]; memcpy(message, &bufin[offs++], message_len); message[message_len+1] = 0; if (!(message[0] == '&' && message[1] == '|' && message[2] == '+')) wprintw(wlog, "\n %s> %s", username, message); wrefresh(wlog); } } else if (bufin[p+2] == CMD_WHISPER && bufin[p+1] == 0x01) { int muted = false; char *username = new char[16]; 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; for (std::vector::iterator v = mutes.begin(); v != mutes.end(); ++v) if (strcmp(username, *v) == 0) { muted = true; break; } if (!muted) { offs+=username_len; message = new char[250-username_len]; int message_len = bufin[offs++]; memcpy(message, &bufin[offs++], message_len); message[message_len] = 0; wprintw(wlog, "\n [%s] -> %s", username, message); } } 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_REGOBJID && bufin[p+1] == 0xff) { char *longID = new char[16]; 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) { break; } 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++]; rooms[longID] = shortID; roomID = shortID; wprintw(wlog, "\n*Joining room %s", longID); for (const auto& [key, value] : objects) { objects.erase((char)key); } teleport(sock_fd, xPos, yPos, zPos, rot); } else if (bufin[p+2] == CMD_BUDDYNTF && bufin[p+1] == 0x01) { char* username = new char[24]; int offs = p+3; int username_len = bufin[offs++]; memcpy(username, &bufin[offs], username_len); username[username_len] = 0; offs+=username_len; bool status = (bufin[offs++] == 1); wprintw(wlog, "\n*%s is %s", username, status?"ONLINE":"OFFLINE"); } 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 { //wprintw(wlog, "\n*LEN[%i] OID[%i] TYPE[%02X] OFF[%i]", bufin[p], bufin[p+1], bufin[p+2], p); } pos_form_cursor(form); wrefresh(wform); wrefresh(wlog); refresh(); 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_fd, char* name, char* pass) { unsigned char bufout[BUFFERSIZE] = {0}; bufout[1] = 0x01; bufout[2] = CMD_SESSINIT; int l = 3; // Username bufout[l++] = 2; bufout[l++] = strlen(name); for (int c = 0; c < strlen(name); c++) bufout[l++] = name[c]; // Password bufout[l++] = 6; bufout[l++] = strlen(pass); for (int c = 0; c < strlen(pass); c++) bufout[l++] = pass[c]; // Protocol bufout[l++] = 3; bufout[l++] = strlen(protocol); for (int c = 0; c < strlen(protocol); c++) bufout[l++] = protocol[c]; // Avatars bufout[l++] = 7; bufout[l++] = strlen(avatars); for (int c = 0; c < strlen(avatars); c++) bufout[l++] = avatars[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_fd, bufout, 0); } void sessExit(int sock_fd) { unsigned char bufout[BUFFERSIZE] = {0}; bufout[0] = 0x03; bufout[1] = 0x01; bufout[2] = CMD_SESSEXIT; wsend(sock_fd, bufout, 0); } void constructPropertyList(int type, std::map props, unsigned char* snd) { snd[1] = 0x01; snd[2] = type; int l = 3; for (const auto& [key, value] : props) { snd[l++] = key; snd[l++] = strlen(value); for (int c = 0; c < strlen(value); c++) snd[l++] = value[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; } } /* void readPropertyList(unsigned char* in) { int l {3}; for (int p = 0; p < in[3]; p++) { 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; } } */ void setAvatar(int sock_fd, char* avatar) { unsigned char bufav[BUFFERSIZE] = {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++] = strlen(avatar); for (int c = 0; c < strlen(avatar); c++) bufav[l++] = avatar[c]; bufav[0] = l; bufav[l+1] = 0; wsend(sock_fd, bufav, 0); } void roomIDReq(int sock_fd, char* room) { char* fullRoom = dimAdd(room); // Don't do this, rooms with new IDs won't update. //if (rooms.find(fullRoom) != rooms.end()) { // roomID = rooms[fullRoom]; //} else { unsigned char bufrm[BUFFERSIZE] = {0}; bufrm[1] = 1; bufrm[2] = CMD_ROOMIDREQ; bufrm[3] = strlen(fullRoom); int x = 4; for (int z = 0; z < strlen(fullRoom); z++) bufrm[x++] = fullRoom[z]; bufrm[x++] = 0; bufrm[0] = x; wsend(sock_fd, bufrm, 0); //} } void teleport(int sock_fd, 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_fd, buftp, 0); } char* dimAdd(char* room) { char* output = new char[BUFFERSIZE]; int l = 0; for (int s = 0; s < strlen(room); s++) output[l++] = room[s]; output[l++] = '<'; for (int s = 0; s < strlen(dimension); s++) output[l++] = dimension[s]; output[l++] = '>'; output[l++] = 0; return output; } void userEnter(char id) { if (!((Drone)objects[id]).droneActive) { wprintw(wlog, "\n+%s has entered the room.", objects[id].name); objects[id].droneActive = true; } } void userExit(char id) { if (((Drone)objects[id]).droneActive) { wprintw(wlog, "\n-%s has left the room.", objects[id].name); objects.erase((char)id); } } int wsend(int sock_fd, unsigned char str[], int flags) { return send(sock_fd, str, str[0], flags); }