diff --git a/Makefile b/Makefile index 606bbff..0bccd36 100644 --- a/Makefile +++ b/Makefile @@ -1,16 +1,17 @@ +CC = gcc clientsrc = $(wildcard src/misc/*.c) \ src/SilverMUDClient.c clientobj = $(clientsrc:.c=.o) serversrc = $(wildcard src/misc/*.c) \ src/SilverMUDServer.c serverobj = $(serversrc:.c=.o) -CLIENTLDFLAGS= -lpthread - +CLIENTLDFLAGS= -lpthread -lncurses +SERVERLDFLAGS= -lncurses SilverMUDClient: $(clientobj) gcc -o $@ $^ $(CLIENTLDFLAGS) SilverMUDServer: $(serverobj) - gcc -o $@ $^ + gcc -o $@ $^ $(SERVERLDFLAGS) .PHONY: clean clean: diff --git a/src/SilverMUDClient.c b/src/SilverMUDClient.c index ea264fc..bde2293 100644 --- a/src/SilverMUDClient.c +++ b/src/SilverMUDClient.c @@ -3,50 +3,87 @@ #include #include #include +#include +#include #include +#include #include #include #include "misc/texteffects.h" +#include "misc/inputhandling.h" #define MAX 1024 #define PORT 5000 #define SA struct sockaddr -void * messageSender(void * sockfd) +// A struct for passing arguments to our threads containing a file descriptor and a window pointer: +typedef struct threadparameters { + int socketDescriptor; + WINDOW * window; +} threadparameters; + +// A globally availible exit boolean. +bool shouldExit = false; + +void sigintHandler(int signal) +{ + shouldExit = true; +} + +void * messageSender(void * parameters) +{ + // Takes user input in a window, sanatizes it, and sends it to the server: + struct threadparameters *threadParameters = parameters; char sendBuffer[MAX]; int characterindex; - - while (1) + + while (!shouldExit) { bzero(sendBuffer, MAX); - printf("COMM-LINK> "); - fgets(sendBuffer, MAX, stdin); - if(sendBuffer[0] != '\n'); + wprintw(threadParameters->window, "\n\n\nCOMM-LINK> "); + if(wgetnstr(threadParameters->window, sendBuffer, MAX) == ERR) { - write((long)sockfd, sendBuffer, MAX); + // Quit if there's any funny business with getting input: + pthread_exit(NULL); } + userInputSanatize(sendBuffer, MAX); + if(sendBuffer[0] == '\n') + { + continue; + } + write(threadParameters->socketDescriptor, sendBuffer, MAX); } + pthread_exit(NULL); } -void * messageReceiver(void * sockfd) +void * messageReceiver(void * parameters) { + // Takes messages from the server and prints them to the chat log window: + struct threadparameters *threadParameters = parameters; char receiveBuffer[MAX]; - while (1) + while (!shouldExit) { - read((long)sockfd, receiveBuffer, MAX); - slowprint("\nUSER-MESSAGE: ", 8000); - slowprint(receiveBuffer, 8000); - slowprint("\nCOMM-LINK (CONT.)> ", 8000); + read(threadParameters->socketDescriptor, receiveBuffer, MAX); + slowPrintNcurses("USER-MESSAGE: ", 8000, threadParameters->window); + slowPrintNcurses(receiveBuffer, 8000, threadParameters->window); bzero(receiveBuffer, MAX); } + pthread_exit(NULL); } int main(int argc, char **argv) { int sockfd, connfd; struct sockaddr_in servaddr, cli; - pthread_t messagingThread; + pthread_t sendingThread; + pthread_t receivingThread; + + // Set the SIGINT handler. + signal(SIGINT, sigintHandler); + + // Print welcome message: + slowPrint("\n--==== \033[33;40mSILVERKIN INDUSTRIES\033[0m COMM-LINK CLIENT ====--\nVersion Alpha 0.2\n", 5000); // Give me a socket, and make sure it's working: sockfd = socket(AF_INET, SOCK_STREAM, 0); @@ -57,7 +94,7 @@ int main(int argc, char **argv) } else { - slowprint("Socket successfully created.\n", 8000); + slowPrint("Socket successfully created.\n", 8000); } bzero(&servaddr, sizeof(servaddr)); @@ -76,18 +113,59 @@ int main(int argc, char **argv) // Connect the server and client sockets, Kronk: if (connect(sockfd, (SA*)&servaddr, sizeof(servaddr)) != 0) { - slowprint("Connection with the Silverkin Industries Comm-Link Server Failed:\nPlease contact your service representative.\n", 8000); + slowPrint("Connection with the Silverkin Industries Comm-Link Server Failed:\nPlease contact your service representative.\n", 8000); exit(0); } else { - slowprint("Connected to the Silverkin Industries Comm-Link Server:\nHave a pleasant day.\n", 8000); + slowPrint("Connected to the Silverkin Industries Comm-Link Server:\nHave a pleasant day.\n", 8000); } + usleep(100000); - // Run a thread to send messages, and use main to recieve. - pthread_create(&messagingThread, NULL, messageSender, (void *)(long)sockfd); - messageReceiver((void *)(long)sockfd); + // Setup Ncurses: + initscr(); + + // Create two pointers to structs to pass arguments to the threads: + threadparameters * logArea; + threadparameters * messageArea; - // Close the socket. + logArea = malloc(sizeof(*logArea)); + messageArea = malloc(sizeof(*messageArea)); + + // Make the windows for the structs, and pass the socket descriptor: + logArea->window = newwin(LINES - 5, COLS - 2, 1, 1); + logArea->socketDescriptor = sockfd; + messageArea->window = newwin(3, COLS, LINES - 3, 0); + messageArea->socketDescriptor = sockfd; + + // Set the two windows to scroll: + scrollok(logArea->window, true); + scrollok(messageArea->window, true); + + // Run a thread to send messages, and use main to recieve: + pthread_create(&sendingThread, NULL, messageSender, messageArea); + pthread_create(&receivingThread, NULL, messageReceiver, logArea); + + // Wait for SIGINT to change + while(!shouldExit) + { + sleep(250); + } + + // Close the threads: + pthread_cancel(sendingThread); + pthread_cancel(receivingThread); + + // Close the socket: close(sockfd); + + // Free the structs: + free(logArea); + free(messageArea); + + // Unsetup Ncurses: + endwin(); + + // Say Goodbye: + slowPrint("\nThank you for choosing Silverkin Industries, valued customer!\n", 8000); } diff --git a/src/SilverMUDServer.c b/src/SilverMUDServer.c index df4783c..3eb8724 100644 --- a/src/SilverMUDServer.c +++ b/src/SilverMUDServer.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -27,8 +28,8 @@ int main() struct sockaddr_in serverAddress, clientAddress; // Give an intro: Display the Silverkin Industries logo and splash text. - slowprint(logostring, 3000); - slowprint("\n--==== \033[33;40mSILVERKIN INDUSTRIES\033[0m COMM-LINK SERVER ====--\nVersion Alpha 0.1\n", 5000); + slowPrint(logostring, 3000); + slowPrint("\n--==== \033[33;40mSILVERKIN INDUSTRIES\033[0m COMM-LINK SERVER ====--\nVersion Alpha 0.2\n", 5000); // Initialize the sockets to 0, so we don't crash. for (int index = 0; index < maxClients; index++) @@ -46,7 +47,7 @@ int main() else { - slowprint(" Socket creation is \033[32;40mGREEN.\033[0m\n", 5000); + slowPrint(" Socket creation is \033[32;40mGREEN.\033[0m\n", 5000); } bzero(&serverAddress, sizeof(serverAddress)); @@ -64,7 +65,7 @@ int main() } else { - slowprint(" Socket binding is \033[32;40mGREEN.\033[0m\n", 5000); + slowPrint(" Socket binding is \033[32;40mGREEN.\033[0m\n", 5000); } // Let's start listening: @@ -75,7 +76,7 @@ int main() } else { - slowprint(" Server listening is \033[32;40mGREEN.\033[0m\n", 5000); + slowPrint(" Server listening is \033[32;40mGREEN.\033[0m\n", 5000); } length = sizeof(clientAddress); @@ -171,7 +172,7 @@ int main() fflush(stdout); for (int sendIndex = 0; sendIndex < clientsAmount; sendIndex++) { - if(sendIndex != i && clientSockets[sendIndex] != STDIN_FILENO && clientSockets[sendIndex] != STDOUT_FILENO && clientSockets[sendIndex] != STDERR_FILENO) + if(clientSockets[sendIndex] != STDIN_FILENO && clientSockets[sendIndex] != STDOUT_FILENO && clientSockets[sendIndex] != STDERR_FILENO) { write(clientSockets[sendIndex], receiveBuffer, sizeof(receiveBuffer)); } diff --git a/src/misc/inputhandling.c b/src/misc/inputhandling.c new file mode 100644 index 0000000..0f4824b --- /dev/null +++ b/src/misc/inputhandling.c @@ -0,0 +1,16 @@ +// inputhandling.c: Implementation of input handling library for SilverMUD. +// Barry Kane, 2021. +#include + +void userInputSanatize(char * inputString, int length) +{ + for(int index = 0; index <= length; index++) + { + if(!isprint(inputString[index])) + { + inputString[index] = '\n'; + inputString[index + 1] = '\0'; + break; + } + } +} diff --git a/src/misc/inputhandling.h b/src/misc/inputhandling.h new file mode 100644 index 0000000..95c9bca --- /dev/null +++ b/src/misc/inputhandling.h @@ -0,0 +1,10 @@ +// inputhandling.h: Header file for the inputhandling library for SilverMUD. +// Barry Kane, 2021 +#ifndef INPUTHANDLING_H +#define INPUTHANDLING_H +#include + +// Sanatize user input to ensure it's okay to send to the server: +void userInputSanatize(char * inputString, int length); + +#endif diff --git a/src/misc/texteffects.c b/src/misc/texteffects.c index 23f03b8..875b3cc 100644 --- a/src/misc/texteffects.c +++ b/src/misc/texteffects.c @@ -2,8 +2,9 @@ // Barry Kane, 2021. #include #include +#include -void slowprint(char * stringToPrint, int delay) +void slowPrint(char * stringToPrint, int delay) { int characterIndex = 0; while(stringToPrint[characterIndex] != '\0') @@ -15,3 +16,17 @@ void slowprint(char * stringToPrint, int delay) characterIndex++; } } + +void slowPrintNcurses(char * stringToPrint, int delay, WINDOW * window) +{ + int characterIndex = 0; + while(stringToPrint[characterIndex] != '\0') + { + waddch(window, stringToPrint[characterIndex]); + // Refresh the ncurses screen. + wrefresh(window); + usleep(delay); + characterIndex++; + } + wrefresh(window); +} diff --git a/src/misc/texteffects.h b/src/misc/texteffects.h index b04a84b..d75e92c 100644 --- a/src/misc/texteffects.h +++ b/src/misc/texteffects.h @@ -1,9 +1,15 @@ +// texteffects.h: Header file for the texteffects library for SilverMUD. +// Barry Kane, 2021. #ifndef TEXTEFFECTS_H_ #define TEXTEFFECTS_H_ #include +#include // A fancy, character by character print. Similar to a serial terminal with lower baud rate. -void slowprint(char * stringToPrint, int delay); +void slowPrint(char * stringToPrint, int delay); + +// The same, altered to work with Ncurses. +void slowPrintNcurses(char * stringToPrint, int delay, WINDOW * window); // A string containing an ASCII art version of the Silverkin Industries logo. char * logostring = " ///////\n //////////////////////////////////////////\n ///////////////////////////////////////////////////////////\n ////////// ////////////////////////////\n ### # # # # ##### ### # # # # # /////////////////\n ### # # # # ## # # ## # ## # //////////////\n ## # # # # # ### # # # # # # /////////\n #### # ### # ##### # # # # # # ## ///////\n # ## # ##### # # ### ### ### # ##### ### ////// \n # # # # # # # # ## # # # # ## ## ////\n # # # # # # # # ## # ### # # ## //\n # # ### ##### ##### ### # # # # #### ### //\n";