#include #include #include #include #include #include #include #include #include #include #include #include #include "scolor.hpp" using namespace std; // Note: Making this kind of thing global is usually not thread-safe char general_buffer[40000]; /* * This is a very read-heavy way to go * It does work */ void examine_server_v1(int fd){ char type; // or uint8_t ssize_t readlen; while(1){ if((readlen = read(fd, &type, 1)) != 1) goto err; if(type == 11){ uint16_t initial_points, stat_limit, description_length; if(recv(fd, &initial_points, 2, MSG_WAITALL) != 2) goto err; if(recv(fd, &stat_limit, 2, MSG_WAITALL) != 2) goto err; if(recv(fd, &description_length, 2, MSG_WAITALL) != 2) goto err; char description[description_length + 1]; if(recv(fd, &description, description_length, MSG_WAITALL) != description_length) goto err; description[description_length] = 0; printf("Game received. \nInitial points: %d\nStat limit: %d\nDescription: %s\n", initial_points, stat_limit, description); printf("Disconnecting, since we finished\n"); return; // What if we wanted to return our game description? } else if(type == 14){ uint8_t major_version, minor_version; uint16_t extension_length; if(read(fd, &major_version, 1) != 1) goto err; if(read(fd, &minor_version, 1) != 1) goto err; if(recv(fd, &extension_length, 2, MSG_WAITALL) != 2) goto err; if(extension_length){ char extensions[extension_length]; if(recv(fd, extensions, extension_length, MSG_WAITALL) != extension_length) goto err; } printf("Connected to lurk server with version %d.%d and %d bytes of extensions\n", major_version, minor_version, extension_length); } else { printf("Received type %d, probably not a lurk server\n", type); return; } } err: printf("Error encountered, giving up\n"); } struct game { uint16_t initial_points, stat_limit, description_length; std::string description; bool read(int fd){ if(recv(fd, this, 6, MSG_WAITALL) != 6) return false; char tmp_description[description_length + 1]; tmp_description[description_length] = 0; if(recv(fd, tmp_description, description_length, MSG_WAITALL) != description_length) return false; description = tmp_description; // Will probably cause a heap allocation return true; } } __attribute__((packed)); struct character { char name[32]; uint8_t flags = 0; uint16_t attack = 0, defense = 0, regen = 0; int16_t health = 0; uint16_t gold = 0, room_number = 0, description_length = 0; char *description; bool need_free = false; bool send(int fd){ uint8_t type = 10; if(write(fd, &type, 1) != 1) return false; if(write(fd, this, 47) != 47) return false; if(write(fd, description, description_length) != description_length) return false; return true; } bool read(int fd){ // We'll assume that the type has already been read if(recv(fd, this, 47, MSG_WAITALL) != 47) return false; if(need_free) free(description); description = (char*)malloc(description_length); need_free = true; if(recv(fd, description, description_length, MSG_WAITALL) != description_length) return false; return true; } ~character(){ if(need_free) free(description); } } __attribute__((packed)); struct version{ uint8_t major_version, minor_version; uint16_t extension_length; void send(int fd){ // code that sends ourselves to fd // Send type, next send ourselves with the "this" pointer: // write(fd, this, 4); } bool read(int fd){ return recv(fd, this, sizeof(struct version), MSG_WAITALL) == 4; } } __attribute__((packed)); struct game get_game_struct(int fd){ char type; // or uint8_t struct game g = {0, 0, 0, ""}; ssize_t readlen; while(1){ if((readlen = read(fd, &type, 1)) != 1) goto err; if(type == 14){ struct version v; if(!v.read(fd)) goto err; printf("Connected to lurk server with version %d.%d and %d bytes of extensions\n", v.major_version, v.minor_version, v.extension_length); } else if (type == 11){ g.read(fd); printf("Game received. \nInitial points: %d\nStat limit: %d\nDescription: %s\n", g.initial_points, g.stat_limit, g.description.c_str()); return g; } else { printf("Received type %d, probably not a lurk server\n", type); return g; } } err: printf("Error encountered, giving up\n"); return g; } struct room_we_found { char name[32]; uint16_t number; bool explored = false; vector connections; room_we_found(char* n, uint16_t num) : number(num) { strcpy(name, n); } }; vector found_rooms; void output_room_list(){ for(auto r : found_rooms){ if(r.explored) printf(BLUE("Room %d (%s), connects to: ").c_str(), r.number, r.name); else printf(YELLOW("Room %d (%s), connects to: ").c_str(), r.number, r.name); for(auto n : r.connections) printf(" %d ", n); printf("\n"); } } /* Alternative: What about a hash table, like an STL map or whatnot? */ ssize_t find_room(uint16_t room_number){ for(size_t i = 0; i < found_rooms.size(); i++) if(found_rooms[i].number == room_number) return i; return -1; } uint16_t our_room; // Not ok if we expand this to support multiple bots uint16_t two = 2; bool go_somewhere(int skt){ if(found_rooms.size() == 0) { printf(RED("There aren't any rooms to go to!\n").c_str()); return false; } if(found_rooms[find_room(our_room)].connections.size() == 0) return false; // There aren't any connections from this room for(auto rn : found_rooms[find_room(our_room)].connections) if(!found_rooms[find_room(rn)].explored){ // go to this room write(skt, &two, 1); write(skt, &(found_rooms[find_room(rn)].number), 2); return true; } uint16_t idx = random() % found_rooms[find_room(our_room)].connections.size(); write(skt, &two, 1); write(skt, &(found_rooms[find_room(our_room)].connections[idx]), 2); return true; } void movement(int skt){ while(true){ sleep(1); go_somewhere(skt); } } int main(int argc, char ** argv){ if(argc < 3){ printf("Usage: %s hostname port\n", argv[0]); return 1; } struct sockaddr_in sad; sad.sin_port = htons(atoi(argv[2])); sad.sin_family = AF_INET; printf("port: %d\n", sad.sin_port); 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; // -> is like (*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 game g = get_game_struct(skt); struct character c; strcpy(c.name, "Lurk Bot Class Demo"); c.attack = g.initial_points/3; c.defense = g.initial_points/3; c.regen = g.initial_points/3; c.description = "Lurk bot from a demo in class"; c.description_length = strlen(c.description); c.send(skt); uint8_t accept[2]; uint8_t start = 6; thread mt(movement, skt); read(skt, &accept, 1); // Might be a good idea to check and see if this worked if(accept[0] == 14){ struct version v; if(!v.read(skt)) goto out; printf("Connected to lurk server with version %d.%d and %d bytes of extensions\n", v.major_version, v.minor_version, v.extension_length); read(skt, &accept, 1); } read(skt, &accept[1], 1); // Might be a good idea to check and see if this worked if(accept[0] != 8){ printf("Read unexpected type %d\n", accept[0]); goto out; } else { printf("Server accepted type %d\n", accept[1]); } write(skt, &start, 1); uint8_t type; while(true){ if(read(skt, &type, 1) != 1) goto out; if(type == 8){ if(read(skt, &type, 1) != 1) goto out; printf("Server accepted a type %d\n", type); } else if (type == 7) { printf("Server sent an error, so we're going to leave\n"); // To make the bot better, print out the error, and figure out how to handle it too goto out; } else if(type == 10) { struct character rc; rc.read(skt); if(!strcmp(rc.name, c.name)){ printf(GREEN("The server sent us a type 10 about us! We're in room %d\n").c_str(), rc.room_number); our_room = rc.room_number; } else { printf("The server sent us a type 10 about %s, in room %d\n", rc.name, rc.room_number); } } else if(type == 9 || type == 13){ printf("Server sent us a room or connection, type %d\n", type); uint16_t number; if(recv(skt, &number, 2, MSG_WAITALL) != 2) goto out; if(recv(skt, general_buffer, 32, MSG_WAITALL) != 32) goto out; printf("Received a struct regarding room %d, %s\n", number, general_buffer); ssize_t room_idx = find_room(number); if(room_idx == -1){ found_rooms.push_back(room_we_found(general_buffer, number)); printf("This room was new!\n"); } if(type == 13){ // Add this connection to the list of connections for the room we're in for(auto n : found_rooms[find_room(our_room)].connections) if(n == number) goto skip_add; found_rooms[find_room(our_room)].connections.push_back(number); skip_add: output_room_list(); } else if(type == 9){ our_room = number; found_rooms[find_room(number)].explored = true; } if(recv(skt, &number, 2, MSG_WAITALL) != 2) goto out; if(recv(skt, general_buffer, number, MSG_WAITALL) != number) goto out; general_buffer[number] = 0; puts(general_buffer); /* If we got a type 13, then it's a connection, and we could send a type 2 and go there! * We could also start making a map of connections on this server */ /* Record what we just learned, if anything */ } else if(type == 1){ printf("Server sent us a message\n"); uint16_t message_length; if(recv(skt, &message_length, 2, MSG_WAITALL) != 2) goto out; message_length += 64; if(recv(skt, general_buffer, message_length, MSG_WAITALL) != message_length) goto out; } } out: close(skt); return 0; }