#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <allegro5/allegro.h>
#include <allegro5/allegro_font.h>
#include <allegro5/allegro_ttf.h>
#include "SDL_net.h"
ALLEGRO_DISPLAY *display;
ALLEGRO_EVENT_QUEUE *event_queue;
ALLEGRO_TIMER *timer;
ALLEGRO_EVENT ev;
ALLEGRO_FONT *font;
Uint8 * dotQuad; // Listening IP
const unsigned short PORT = 1337; // The port our server will listen for incoming connections on
const unsigned short BUFFER_SIZE = 512; // Size of our message buffer
const unsigned short MAX_SOCKETS = 500; // Max number of sockets
const unsigned short MAX_CLIENTS = MAX_SOCKETS - 1; // Max number of clients in our socket set (-1 because we place the servers listening socket as the first socket in the set)
// Messages we recieve from or send back to any connecting(ed) client
const std::string SERVER_NOT_FULL = "OK";
const std::string SERVER_FULL = "SERVER FULL";
std::string text = "";
std::string text2 = "";
bool serverRunning = true;
bool redraw = false;
int clientCount = 0; // Count of how many clients are currently connected to the server.
int main(int argc, char **argv) {
std::ofstream file("Errors.txt");
file << "Program initialized" << std::endl;
if (!al_init()) {
file << "Failed to initialize Allegro!" << std::endl;
return -1;
}
//Create ourself a window
display = al_create_display(640, 480);
if (!display) {
file << "Failed to initialize Display!" << std::endl;
al_destroy_display(display);
return -1;
}
event_queue = al_create_event_queue();
if (!event_queue) {
file << "Failed to initialize Event Queue!" << std::endl;
al_destroy_event_queue(event_queue);
return -1;
}
timer = al_create_timer(1.0 / 60.0);
if (!timer) {
file << "Failed to initialize Timer!" << std::endl;
al_destroy_timer(timer);
return -1;
}
if (SDLNet_Init() == -1) {
file << "Failed to initialize SDL_Net!" << std::endl;
return -1;
}
IPaddress serverIP; // The ip of the server (0.0.0.0 - any IP)
TCPsocket serverSocket; // The server socket that clients will use to coonect to us
TCPsocket clientSocket[MAX_CLIENTS]; // An array of sockets for the clients.
bool isSocketFree[MAX_CLIENTS]; // An array of flags to keep track of which client sockets are free
char buffer[BUFFER_SIZE]; // Array of characters used to store the messages we receive
int receivedByteCount = 0; // A variable to keep track of how many bytes (i.e. characters) we need to read for any given incoming message i.e. the size of the incoming data
std::string clientcount_string = "none";
std::stringstream myString;
// Create a socket set with enough space to store our desired number of connections (sockets)
SDLNet_SocketSet socketSet = SDLNet_AllocSocketSet(MAX_SOCKETS);
if (!socketSet) {
file << "Failed to allocate Socket set!" << std::endl;
}
else
{
file << "Socket set allocated with size: " << MAX_SOCKETS << ", of which " << MAX_CLIENTS << " are available for use by clients." << std::endl;
}
// Initialize all the sockets (i.e. blank them ready for use!)
for (int i = 0; i < MAX_CLIENTS; i++) {
clientSocket[i] = NULL;
isSocketFree[i] = TRUE; // Set all our sockets to be free (i.e. available for use for new client connections)
}
al_init_font_addon();
al_init_ttf_addon();
font = al_load_font("data/fonts/bookosb.ttf", 24, 0);
al_register_event_source(event_queue, al_get_display_event_source(display));
al_register_event_source(event_queue, al_get_timer_event_source(timer));
al_start_timer(timer);
int hostResolved = SDLNet_ResolveHost(&serverIP, NULL, PORT);
if (hostResolved == -1) {
file << "Failed to resolve the server host!" << std::endl;
text = "Server not started.";
}
else {
dotQuad = (Uint8*)&serverIP.host;
file << "Successfully resolved host to IP: " << (unsigned short)dotQuad[0] << "." << (unsigned short)dotQuad[1] << "." << (unsigned short)dotQuad[2] << "." << (unsigned short)dotQuad[3] << ":" << PORT << std::endl;
text = "Server started.";
}
serverSocket = SDLNet_TCP_Open(&serverIP);
if (!serverSocket) {
file << "Failed to open the server socket." << std::endl;
text2 = "Failed to open socket.";
}
else {
file << "Successfully created the server socket." << std::endl;
text2 = "Waiting for clients.";
}
// Add our server socket to the socket set
SDLNet_TCP_AddSocket(socketSet, serverSocket);
file << "Waiting for clients..." << std::endl;
do {
// Check for activity on the entire socket set. The second parameter is the number of milliseconds to wait for.
// For the wait-time, 0 means do not wait (high CPU!), -1 means wait for up to 49 days (no, really), and any other number is a number of milliseconds, i.e. 5000 means wait for 5 seconds
int numActiveSockets = SDLNet_CheckSockets(socketSet, 0);
if (numActiveSockets != 0) {
file << "There are currently " << numActiveSockets << " socket(s) with data to be processed." << std::endl;
}
// Check if our server socket has received any daya
// Note: SocketReady can only be called on a socket which is part of a set and that has CheckSocket called on it (the set, that is)
// SDLNet_SocketRead returns non-zero for activity, and zero is returned for no activity. Which is a bit bass-ackwards IMHO, but there you go.
int serverSocketActivity = SDLNet_SocketReady(serverSocket);
//file << "Just checked client number " << clientNumber << " and received activity status is: " << clientSocketActivity << endl;
// If there is activity on our server socket (i.e. a client has transmitted data to us) then...
if (serverSocketActivity != 0) {
// If we have room for more clients
if (clientCount < MAX_CLIENTS) {
// Find the first free socket in our array of client sockets
int freeSpot = -99;
for (int i = 0; i < MAX_CLIENTS; i++) {
if (isSocketFree[i]) {
file << "Found a free spot at element: " << i << std::endl;
isSocketFree[i] = false; // Set the socket to be taken
freeSpot = i; // Keep the location to add our connection at the index in the array of client sockets
break; // Break out straight away
}
}
// Accept the client connection and then...
clientSocket[freeSpot] = SDLNet_TCP_Accept(serverSocket);
// ... Add the new client socket to the socket set (o.e. the list of sockets we check for activity)
SDLNet_TCP_AddSocket(socketSet, clientSocket[freeSpot]);
// Increase our client count
clientCount++;
// Send a message to the client saying "OK" to indicate the incoming connection has been accepted
strcpy_s(buffer, SERVER_NOT_FULL.c_str());
int msgLength = strlen(buffer) + 1;
SDLNet_TCP_Send(clientSocket[freeSpot], (void *)buffer, msgLength);
file << "Client connected, there are now " << clientCount << " client(s) connected." << std::endl;
}
else { // If we don't have room for new clients..
file << "Maximum client count reached - rejecting client connection!" << std::endl;
// Accept the client connection to clear it form the incoming connections list
TCPsocket tempSock = SDLNet_TCP_Accept(serverSocket);
// Send a message to the client saying "Server is full" to tell the client to go away
strcpy_s(buffer, SERVER_FULL.c_str());
int msgLength = strlen(buffer) + 1;
SDLNet_TCP_Send(tempSock, (void *)buffer, msgLength);
// Shutdown, disconnect, and close the socket to the client
SDLNet_TCP_Close(tempSock);
}
} // End of if server socket has activity check
// Loop to check all the possible client sockets for activity
for (int clientNumber = 0; clientNumber < MAX_CLIENTS; clientNumber++) {
// If the socket is ready (i.e. it has data we can read)... (SDLNet_SocketReady returns non-zero if there is activity on the socket, and zero if there is no activity)
int clientSocketActivity = SDLNet_SocketReady(clientSocket[clientNumber]);
// If there is any activity on the client socket...
if (clientSocketActivity != 0) {
// Check if the client socket has transmitted any data by reading from the socket and placing it in the buffer character array
receivedByteCount = SDLNet_TCP_Recv(clientSocket[clientNumber], buffer, BUFFER_SIZE);
// If there's activity, but we didn't read anything from the client socket, then the client has disconnected...
if (receivedByteCount <= 0)
{
//...so output a suitable message and then...
file << "Client " << clientNumber << " disconnected." << std::endl << std::endl;
//... remove the socket from the socket set, then close and reset the socket ready for re-use and finally...
SDLNet_TCP_DelSocket(socketSet, clientSocket[clientNumber]);
SDLNet_TCP_Close(clientSocket[clientNumber]);
clientSocket[clientNumber] = NULL;
// ...free up their slot so it can be reused...
isSocketFree[clientNumber] = true;
// ...and decrement the count of connected clients.
clientCount--;
file << "Server is now connected to: " << clientCount << " client(s)." << std::endl << std::endl;
}
else // If we read some data from the client socket...
{
// Output the message the server received to the screen
file << "Received: >>>> " << buffer << " from client number: " << clientNumber << std::endl;
// Send message to all other connected clients
int originatingClient = clientNumber;
for (int loop = 0; loop < MAX_CLIENTS; loop++)
{
// Send a message to the client saying "OK" to indicate the incoming connection has been accepted
//strcpy( buffer, SERVER_NOT_FULL.c_str() );
int msgLength = strlen(buffer) + 1;
// If the message length is more than 1 (i.e. client pressed enter without entering any other text), then
// send the message to all connected clients except the client who originated the message in the first place
if (msgLength > 1 && loop != originatingClient && isSocketFree[loop] == false)
{
file << "Retransmitting message: " << buffer << " (" << msgLength << " bytes) to client number: " << loop << std::endl;
SDLNet_TCP_Send(clientSocket[loop], (void *)buffer, msgLength);
}
}
// If the client told us to shut down the server, then set the flag to get us out of the main loop and shut down
if (strcmp(buffer, "shutdown") == 0)
{
serverRunning = false;
file << "Disconnecting all clients and shutting down the server..." << std::endl << std::endl;
}
}
} // End of if client socket is active check
myString.str("");//most awesome way to clear a stringstream
myString << clientCount;//int to stringstream
clientcount_string = myString.str();//stringstream to string
} // End of server socket check sockets loop
// Check for events (Currently keyboard)
al_wait_for_event(event_queue, &ev);
std::string userInput = "";
int status;
if (ev.type == ALLEGRO_EVENT_TIMER) {
redraw = true;
}
if (ev.type == ALLEGRO_EVENT_DISPLAY_CLOSE) {
serverRunning = false; // If we must quit, then do so
}
else if (ev.type == ALLEGRO_EVENT_KEY_DOWN)
{
if (ev.keyboard.keycode == ALLEGRO_KEY_ENTER)
{
if (userInput.length() > 0) { }
}//send the message
}
if (ev.type == ALLEGRO_EVENT_KEY_CHAR) { //Add to the message
status = ev.keyboard.unichar; // Get the keypress
file << "the key pressed was " << status << " " << (char)status << std::endl;
if (status == 8) {
if (userInput.length() > 0) {
userInput.erase(userInput.length() - 1);
}
}
else if (status >= 65 && status <= 122) {
userInput += (char)status;
}
}
else {
status = -1;
}
if (redraw && al_is_event_queue_empty(event_queue)) {
al_clear_to_color(al_map_rgb(0, 0, 0));
al_draw_text(font, al_map_rgb(255, 255, 255), 20, 20, 0, text.c_str());
al_draw_text(font, al_map_rgb(255, 255, 255), 20, 40, 0, text2.c_str());
al_draw_text(font, al_map_rgb(255, 255, 255), 20, 100, 0, userInput.c_str());
al_draw_text(font, al_map_rgb(255, 255, 255), 20, 100, 0, "Hello");
al_flip_display();
}
} while (serverRunning);
file << "Server shutting down" << std::endl;
// Free our socket set (i.e. all the clients in our socket set)
SDLNet_FreeSocketSet(socketSet);
// Close our server socket, cleanup SDL_net and finish!
SDLNet_TCP_Close(serverSocket);
SDLNet_Quit();
al_destroy_font(font);
al_destroy_timer(timer);
al_destroy_event_queue(event_queue);
al_destroy_display(display);
return 0;
}