// Run like this: simple_client address port // Results in argv ["./simple_client", "address", "port"] /* Problems from Monday: * our_player had uninitialized type, causing the server to disconnect it * message sender and receiver were backwards * message was read into &m.message instead of m.message * r_or_c struct didn't include the name */ #include #include #include #include #include #include #include #include #include #include #include "scolor.hpp" #include #include #include #include using namespace std; struct version_message { uint8_t type, major, minor; uint16_t extlen; }__attribute__((packed)); struct game_message { uint8_t type; uint16_t initial_points, stat_limit, description_length; char *description = 0; }__attribute__((packed)); struct our_player { uint8_t type = 10; char name[32] = "Map Bot"; uint8_t flags = 0x80; uint16_t attack=1, defence=1, regen=1; int16_t health; uint16_t gold, room, description_length = 4; char description[4] = "map"; }__attribute__((packed)); struct our_player us; struct character { uint8_t type; char name[32]; uint8_t flags; uint16_t attack, defence, regen; int16_t health; uint16_t gold, room, description_length; char *description = 0; }__attribute__((packed)); struct r_or_c { uint8_t type; uint16_t number; char name[32]; uint16_t description_length; char *description; } __attribute__((packed)); struct message { uint8_t type; uint16_t length; char receiver[32]; char sender[32]; char *message = 0; } __attribute__((packed)); struct error { uint8_t type; uint8_t code; uint16_t length; char error_message[65535]; // What a waste of space! } __attribute__((packed)); bool read_rc(int skt, struct r_or_c *rc){ if(recv(skt, rc, 37, MSG_WAITALL) != 37) return false; if(recv(skt, rc->description, rc->description_length, MSG_WAITALL) != rc->description_length) return false; rc->description[rc->description_length] = 0; return true; } void print_rc(struct r_or_c *rc, char is_room){ printf("Received %s: %d \n%s\n", is_room? "room":"connection", rc->number, rc->description); } map> connection_map; void handle_sigint(int signal){ puts(DGREEN("\nConnection Map:").c_str()); for(auto room : connection_map){ printf(("For room " + YELLOW("%d") + ": ").c_str(), room.first); for(auto cr : room.second) printf(" %d ", cr); puts(""); } exit(0); } bool move_request = false; uint16_t next_room; vector path; void move_thread_function(int skt){ char move_type = 2; int since_move = 0; while(1){ sleep(1); if(move_request){ sleep(1); write(skt, &move_type, 1); write(skt, &next_room, 2); move_request = false; since_move = 0; } since_move++; if(since_move > 3){ move_request = true; path.pop_back(); next_room = path[path.size() - 1]; } } } int main(int argc, char ** argv){ printf("Size of version: %d\n", sizeof(version_message)); if(argc < 3){ printf("Usage: %s hostname port\n", argv[0]); return 1; } struct sigaction action; action.sa_handler = handle_sigint; sigaction(SIGINT, &action, 0); struct sockaddr_in sad; sad.sin_port = htons(atoi(argv[2])); sad.sin_family = AF_INET; int skt = socket(AF_INET, SOCK_STREAM, 0); // do a dns lookup struct hostent* entry = gethostbyname(argv[1]); if(!entry){ if(h_errno == HOST_NOT_FOUND){ printf("This is our own message that says the host wasn't found\n"); } herror("gethostbyname"); return 1; } struct in_addr **addr_list = (struct in_addr**)entry->h_addr_list; struct in_addr* c_addr = addr_list[0]; char* ip_string = inet_ntoa(*c_addr); sad.sin_addr = *c_addr; // copy the address we found into sad // Finally done with DNS! printf("Connecting to: %s\n", ip_string); if( connect(skt, (struct sockaddr*)&sad, sizeof(struct sockaddr_in)) ){ perror("connect"); return 1; } struct version_message v; read(skt, &v, 5); if(v.type != 14){ printf("Unsupported Server\n"); return 1; } printf("Got type %d, version %d.%d with %d bytes of extensions\n", v.type, v.major, v.minor, v.extlen); struct game_message g; recv(skt, &g, 7, MSG_WAITALL); g.description = (char*)malloc(g.description_length + 1); recv(skt, g.description, g.description_length, MSG_WAITALL); g.description[g.description_length] = 0; printf("Received game with ip=%d, sl=%d, dl=%d, description:\n%s\n", g.initial_points, g.stat_limit, g.description_length, g.description); write(skt, &us, sizeof(our_player)); uint8_t type = 6; write(skt, &type, 1); struct message m; struct error e; struct r_or_c rc; struct character c; rc.description = (char*)malloc(65536); // This'll stay allocated. A waste of space? Or time saver? uint16_t current_room = 0; size_t readlen; thread move_thread(move_thread_function, skt); set explored_rooms; while(1){ readlen = recv(skt, &type, 1, MSG_PEEK); if(readlen < 1) break; // printf("Read new type %d (%d bytes)\n", type, readlen); switch(type){ case 1: // A message recv(skt, &m, 67, MSG_WAITALL); if(m.message) free(m.message); m.message = (char*)malloc(m.length + 1); // What a waste of time compared to the way error messages happen! m.message[m.length] = 0; recv(skt, m.message, m.length, MSG_WAITALL); printf("Received message from %s to %s: \n%s\n", m.sender, m.receiver, m.message); break; case 7: // Error recv(skt, &e, 4, MSG_WAITALL); recv(skt, e.error_message, e.length, MSG_WAITALL); e.error_message[e.length] = 0; printf("Received error type %d: \n%s\n", e.code, e.error_message); break; case 8: // Accept read(skt, &type, 1); // The 8, for accept read(skt, &type, 1); // The type that the server accepted printf("The server accepted a type %d\n", type); break; case 9: // Room read_rc(skt, &rc); print_rc(&rc, 1); break; case 10: // Character recv(skt, &c, 48, MSG_WAITALL); if(c.description) free(c.description); c.description = (char*)malloc(c.description_length + 1); c.description[c.description_length] = 0; recv(skt, c.description, c.description_length, MSG_WAITALL); if(!strcmp("Map Bot", c.name)){ current_room = c.room; explored_rooms.insert(current_room); printf(BLUE("We're now in room %d\n").c_str(), current_room); } if(!path.size() || path[path.size() - 1] != current_room) { path.push_back(current_room); printf(RED("Current Path: ").c_str()); for(auto i : path) printf(" %d ", i); puts(""); } printf("Received character %s\n", c.name); break; case 13: // Connection if(!read_rc(skt, &rc)) break; print_rc(&rc, 0); if(!connection_map.contains(current_room)) connection_map[current_room] = set(); if(!explored_rooms.contains(rc.number)){ next_room = rc.number; move_request = true; } if(!connection_map[current_room].contains(rc.number)) connection_map[current_room].insert(rc.number); break; default: printf("Received unexpected type %d\n", type); return 1; } } close(skt); return 0; }