diff --git a/.gitignore b/.gitignore index 6d5206b..7d9403b 100644 --- a/.gitignore +++ b/.gitignore @@ -49,4 +49,63 @@ modules.order Module.symvers Mkfile.old -dkms.conf \ No newline at end of file +dkms.conf + +# http://www.gnu.org/software/automake + +Makefile.in +/ar-lib +/mdate-sh +/py-compile +/test-driver +/ylwrap +.deps/ +.dirstamp + +# http://www.gnu.org/software/autoconf + +autom4te.cache +/autoscan.log +/autoscan-*.log +/aclocal.m4 +/compile +/config.cache +/config.guess +/config.h.in +/config.log +/config.status +/config.sub +/configure +/configure.scan +/depcomp +/install-sh +/missing +/stamp-h1 + +# https://www.gnu.org/software/libtool/ + +/ltmain.sh + +# http://www.gnu.org/software/texinfo + +/texinfo.tex + +# http://www.gnu.org/software/m4/ + +m4/libtool.m4 +m4/ltoptions.m4 +m4/ltsugar.m4 +m4/ltversion.m4 +m4/lt~obsolete.m4 + +# Generated Makefile +# (meta build system like autotools, +# can automatically generate from config.status script +# (which is called by configure script)) +Makefile + +SilverMUDServer +SilverMUDClient +config.h +config.h.in +stamp-h1 \ No newline at end of file diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..6f59a34 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,17 @@ +bin_PROGRAMS = SilverMUDServer SilverMUDClient +dist_doc_DATA = README.org +SilverMUDServer_CFLAGS = -lgnutls -g $(GUILE_CFLAGS) $(GUILE_LIBS) +SilverMUDClient_CFLAGS = -lgnutls -g -lncurses $(GUILE_CFLAGS) $(GUILE_LIBS) + +SilverMUDServer_SOURCES = \ + source/messages.c \ + source/server/player-data.c \ + source/server/connections.c \ + source/server/scheme-integration.c \ + source/server/main.c + +SilverMUDClient_SOURCES = \ + source/messages.c \ + source/client/client-drawing.c \ + source/client/receiving-thread.c \ + source/client/main.c diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..1151bc2 --- /dev/null +++ b/configure.ac @@ -0,0 +1,9 @@ +AC_INIT([SilverMUD], [0.0.1], [barra@ocathain.ie]) +AM_INIT_AUTOMAKE([-Wall -Werror foreign subdir-objects]) +AC_PROG_CC +AC_CONFIG_HEADERS([source/config.h]) +AC_CONFIG_FILES([ + Makefile +]) +PKG_CHECK_MODULES([GUILE], [guile-3.0]) +AC_OUTPUT diff --git a/documentation/.placeholder b/documentation/.placeholder deleted file mode 100644 index e69de29..0000000 diff --git a/documentation/SilverMUD-Gamemasters-Guidebook.org b/documentation/SilverMUD-Gamemasters-Guidebook.org new file mode 100644 index 0000000..b3facaf --- /dev/null +++ b/documentation/SilverMUD-Gamemasters-Guidebook.org @@ -0,0 +1,9 @@ +#+TITLE: SilverMUD: Gamemaster's Guidebook +#+SUBTITLE: SilverMUD First Edition +#+AUTHOR: Barra Ó Catháin + +* How To Run SilverMUD, The Software: + +* How To Run SilverMUD, The Game: + +* Creating Content: diff --git a/documentation/SilverMUD-Players-Guidebook.org b/documentation/SilverMUD-Players-Guidebook.org new file mode 100644 index 0000000..cfea4ff --- /dev/null +++ b/documentation/SilverMUD-Players-Guidebook.org @@ -0,0 +1,15 @@ +#+TITLE: SilverMUD: Player's Guidebook +#+SUBTITLE: SilverMUD First Edition +#+AUTHOR: Barra Ó Catháin + +* Getting Started: + +* Basic Commands: + +* Interacting With The World: + +* The Character System: + +* The Combat System: + +* Commands In Depth: diff --git a/documentation/SilverMUD-Programmers-Guidebook.org b/documentation/SilverMUD-Programmers-Guidebook.org new file mode 100644 index 0000000..3120ea4 --- /dev/null +++ b/documentation/SilverMUD-Programmers-Guidebook.org @@ -0,0 +1,10 @@ +#+TITLE: SilverMUD: Programmer's Guidebook +#+SUBTITLE: SilverMUD First Edition +#+AUTHOR: Barra Ó Catháin + +* The Scheme Programming Language: + +* The Basic Concepts Of SilverMUD Programming: + +* Concepts In Detail: + diff --git a/notes/.placeholder b/notes/.placeholder deleted file mode 100644 index e69de29..0000000 diff --git a/notes/SilverMUD-Implementation.org b/notes/SilverMUD-Implementation.org new file mode 100644 index 0000000..113fc46 --- /dev/null +++ b/notes/SilverMUD-Implementation.org @@ -0,0 +1,28 @@ +#+TITLE: SilverMUD Implementation Document +This document contains information about various implementation details of +SilverMUD, as a scratchpad for decisions before implementation. + +* Structures: +** Server->Client Message Format (1409 bytes total): +*** Message Type - Unsigned 8 Bit Integer: +|-------+------------------------| +| Value | Purpose | +|-------+------------------------| +| 0 | System Message | +| 1 | Client Setting Message | +| 2 | Command Output Message | +| 3 | Local Chat Message | +| 4 | Player Chat Message | +| 5 | Party Chat Message | +| 6 | Player Emote Message | +|-------+------------------------| + +Further values remain open for possible additional message types. + +*** Sender Name - 128 Character String. +This field may be repurposed for message types without a need for a name. + +*** Message Content - 1024 Character String. + +** Client->Server Message Format (1024 bytes total): +*** Message Content - 1024 Character String. diff --git a/source/.placeholder b/source/.placeholder deleted file mode 100644 index e69de29..0000000 diff --git a/source/client/.placeholder b/source/client/.placeholder deleted file mode 100644 index e69de29..0000000 diff --git a/source/client/client-drawing.c b/source/client/client-drawing.c new file mode 100644 index 0000000..faa21da --- /dev/null +++ b/source/client/client-drawing.c @@ -0,0 +1,61 @@ +// ========================================== +// | SilverMUD Client - client-drawing.c | +// | Copyright (C) 2023, Barra Ó Catháin | +// | See end of file for copyright notice. | +// ========================================== +#include "../config.h" +#include "client-drawing.h" + +void redrawClientLayout(WINDOW * gameWindow, WINDOW * chatWindow, WINDOW * inputWindow) +{ + int height, width; + getmaxyx(stdscr, height, width); + + // Draw the lines that will seperate windows: + wborder(stdscr, ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '); + attron(A_REVERSE); + mvwhline(stdscr, 0, 0, '=', width); + mvwhline(stdscr, height / 2, 0, '=', width); + mvwhline(stdscr, height - 2, 0, '=', width); + + // Write the labels for windows: + attron(COLOR_PAIR(1)); + mvwprintw(stdscr, 0, 1, " SilverMUD | Version %s ", PACKAGE_VERSION); + mvwprintw(stdscr, height / 2, 1, " Chat "); + mvwprintw(stdscr, height - 2, 1, " Input "); + attroff(COLOR_PAIR(1)); + attroff(A_REVERSE); + + // Move the windows into place: + mvwin(gameWindow, 1, 1); + mvwin(chatWindow, (height / 2) + 1 , 1); + mvwin(inputWindow, height - 1, 1); + + // Resize the windows: + wresize(gameWindow, (height - 2) / 2, width - 2); + wresize(chatWindow, ((height - 4) / 2) - (1 - (height % 2)), width - 2); + wresize(inputWindow, 1, width - 2); + + // Refresh every window: + wrefresh(stdscr); + wrefresh(gameWindow); + wrefresh(chatWindow); + wrefresh(inputWindow); +} + +// ======================================================== +// | End of client-drawing.c, copyright notice follows. | +// ======================================================== + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. + +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . diff --git a/source/client/client-drawing.h b/source/client/client-drawing.h new file mode 100644 index 0000000..dd7a4d4 --- /dev/null +++ b/source/client/client-drawing.h @@ -0,0 +1,28 @@ +// ========================================== +// | SilverMUD Client - client-drawing.h | +// | Copyright (C) 2023, Barra Ó Catháin | +// | See end of file for copyright notice. | +// ========================================== +#ifndef CLIENT_DRAWING_H +#define CLIENT_DRAWING_H +#include + +void redrawClientLayout(WINDOW * gameWindow, WINDOW * chatWindow, WINDOW * inputWindow); + +#endif +// ======================================================== +// | End of client-drawing.h, copyright notice follows. | +// ======================================================== + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. + +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . diff --git a/source/client/main.c b/source/client/main.c new file mode 100644 index 0000000..6b9821c --- /dev/null +++ b/source/client/main.c @@ -0,0 +1,145 @@ +// ========================================= +// | SilverMUD Client - main.c | +// | Copyright (C) 2023, Barra Ó Catháin | +// | See end of file for copyright notice. | +// ========================================= +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../config.h" +#include "../messages.h" +#include "client-drawing.h" +#include "receiving-thread.h" + +int main (int argc, char ** argv) +{ + // Print a welcome message: + printf("SilverMUD Client - Starting Now.\n" + "================================\n"); + + // Create a socket for communicating with the server: + int serverSocket = socket(AF_INET, SOCK_STREAM, 0); + if (serverSocket == -1) + { + printf("Socket creation failed. Aborting.\n"); + exit(EXIT_FAILURE); + } + + // Set up the server address structure to point to the server: + struct sockaddr_in serverAddress; + serverAddress.sin_family = AF_INET; + serverAddress.sin_addr.s_addr = inet_addr("127.0.0.1"); + serverAddress.sin_port = htons(5000); + + // Connect to the server: + if (connect(serverSocket, (struct sockaddr *)&serverAddress, sizeof(struct sockaddr_in)) != 0) + { + printf("Failed to connect to the server. Aborting.\n"); + exit(EXIT_FAILURE); + } + + // Set up a GnuTLS session and handshake with the server: + gnutls_session_t tlsSession = NULL; + if (gnutls_init(&tlsSession, GNUTLS_CLIENT) < 0) + { + exit(EXIT_FAILURE); + } + + gnutls_anon_client_credentials_t clientKey = NULL; + gnutls_anon_allocate_client_credentials(&clientKey); + gnutls_credentials_set(tlsSession, GNUTLS_CRD_ANON, &clientKey); + + gnutls_transport_set_int(tlsSession, serverSocket); + gnutls_priority_set_direct(tlsSession, "PERFORMANCE:+ANON-ECDH:+ANON-DH", NULL); + + gnutls_handshake_set_timeout(tlsSession, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT); + + int returnValue = -1; + do + { + returnValue = gnutls_handshake(tlsSession); + } + while (returnValue < 0 && gnutls_error_is_fatal(returnValue) == 0); + + // Initialize ncurses: + initscr(); + keypad(stdscr, TRUE); + + if (!has_colors()) + { + endwin(); + exit(EXIT_FAILURE); + } + + // Enable colours: + start_color(); + use_default_colors(); + init_pair(1, COLOR_GREEN, -1); + init_pair(2, COLOR_YELLOW, -1); + init_pair(3, COLOR_RED, -1); + init_pair(4, COLOR_BLUE, -1); + init_pair(5, COLOR_CYAN, -1); + init_pair(6, COLOR_MAGENTA, -1); + + // Variables needed for the main loop: + int height, width; + getmaxyx(stdscr, height, width); + struct ClientToServerMessage message; + + WINDOW * chatWindow, * gameWindow, * inputWindow; + inputWindow = newwin(1, width - 2, height - 1, 1); + gameWindow = newwin((height / 2) - 1, width - 2, 1, 1); + chatWindow = newwin((height / 2) - 3, width - 2, (height / 2) + 1, 1); + + scrollok(gameWindow, TRUE); + scrollok(chatWindow, TRUE); + scrollok(inputWindow, TRUE); + + redrawClientLayout(gameWindow, chatWindow, inputWindow); + + struct ReceivingThreadArguments receivingThreadArguments; + receivingThreadArguments.chatWindow = chatWindow; + receivingThreadArguments.gameWindow = gameWindow; + receivingThreadArguments.inputWindow = inputWindow; + receivingThreadArguments.session = tlsSession; + + pthread_t receivingThread; + pthread_create(&receivingThread, NULL, receivingThreadHandler, &receivingThreadArguments); + + while (true) + { + wgetnstr(inputWindow, message.content, 1024); + + if (message.content[0] != '\0') + { + gnutls_record_send(tlsSession, &message, 1024); + } + } + + // Return a successful status code to the operating system: + return 0; +} + +// ============================================ +// | End of main.c, copyright notice follows. | +// ============================================ + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. + +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . diff --git a/source/client/receiving-thread.c b/source/client/receiving-thread.c new file mode 100644 index 0000000..41b78c3 --- /dev/null +++ b/source/client/receiving-thread.c @@ -0,0 +1,87 @@ +// ========================================== +// | SilverMUD Client - receiving-thread.c | +// | Copyright (C) 2023, Barra Ó Catháin | +// | See end of file for copyright notice. | +// ========================================== +#include +#include + +#include "../messages.h" +#include "client-drawing.h" +#include "receiving-thread.h" + +void * receivingThreadHandler(void * threadArguments) +{ + start_color(); + use_default_colors(); + init_pair(1, COLOR_GREEN, -1); + init_pair(2, COLOR_YELLOW, -1); + init_pair(3, COLOR_RED, -1); + init_pair(4, COLOR_BLUE, -1); + init_pair(5, COLOR_CYAN, -1); + init_pair(6, COLOR_MAGENTA, -1); + + // Unpack the thread's arguments: + gnutls_session_t session = ((struct ReceivingThreadArguments *)threadArguments)->session; + WINDOW * chatWindow = ((struct ReceivingThreadArguments *)threadArguments)->chatWindow, + * gameWindow = ((struct ReceivingThreadArguments *)threadArguments)->gameWindow, + * inputWindow = ((struct ReceivingThreadArguments *)threadArguments)->inputWindow; + + // Print a message into the game window: + wprintw(gameWindow, "Connection successful. Welcome to "); + wattrset(gameWindow, COLOR_PAIR(2)); + wprintw(gameWindow, "SilverMUD!\n"); + wattrset(gameWindow, A_NORMAL); + + struct ServerToClientMessage currentMessage; + while (true) + { + gnutls_record_recv(session, ¤tMessage, sizeof(struct ServerToClientMessage)); + + switch (currentMessage.type) + { + case SYSTEM: + { + wattrset(gameWindow, A_BOLD); + wprintw(gameWindow, "%s\n", currentMessage.content); + wattrset(gameWindow, A_NORMAL); + break; + } + case LOCAL_CHAT: + { + wattrset(chatWindow, A_BOLD); + wprintw(chatWindow, "<%s>: ", currentMessage.name); + wattrset(chatWindow, A_NORMAL); + wprintw(chatWindow, "%s\n", currentMessage.content); + break; + } + default: + { + wattrset(chatWindow, A_BOLD); + wprintw(chatWindow, "<%s>: ", currentMessage.name); + wattrset(chatWindow, A_NORMAL); + wprintw(chatWindow, "%s\n", currentMessage.content); + break; + } + } + + redrawClientLayout(gameWindow, chatWindow, inputWindow); + } +} + +// ======================================================== +// | End of receiving-thread.c, copyright notice follows. | +// ======================================================== + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. + +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . diff --git a/source/client/receiving-thread.h b/source/client/receiving-thread.h new file mode 100644 index 0000000..69a4c77 --- /dev/null +++ b/source/client/receiving-thread.h @@ -0,0 +1,35 @@ +// ========================================== +// | SilverMUD Client - receiving-thread.h | +// | Copyright (C) 2023, Barra Ó Catháin | +// | See end of file for copyright notice. | +// ========================================== +#ifndef RECEIVING_THREAD_H +#define RECEIVING_THREAD_H +#include +#include + +struct ReceivingThreadArguments +{ + WINDOW * chatWindow, * gameWindow, * inputWindow; + gnutls_session_t session; +}; + +void * receivingThreadHandler(void * threadArguments); + +#endif +// ======================================================== +// | End of receiving-thread.h, copyright notice follows. | +// ======================================================== + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. + +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . diff --git a/source/messages.c b/source/messages.c new file mode 100644 index 0000000..134c3c5 --- /dev/null +++ b/source/messages.c @@ -0,0 +1,113 @@ +// ========================================= +// | SilverMUD - messages.c | +// | Copyright (C) 2023, Barra Ó Catháin | +// | See end of file for copyright notice. | +// ========================================= +#include +#include +#include + +#include "messages.h" + +// Allocate and initialize a client to server message using the passed values: +struct ClientToServerMessage * createClientToServerMessage(char * content) +{ + // Allocate the needed memory for the message: + struct ClientToServerMessage * newMessage = calloc(1, sizeof(struct ClientToServerMessage)); + + // Copy the string and terminate it: + strncpy(newMessage->content, content, MESSAGE_CONTENT_LENGTH - 1); + newMessage->content[MESSAGE_CONTENT_LENGTH - 1] = '\0'; + + // Return the pointer: + return newMessage; +} + +// A Scheme wrapper for creating client to server messages: +SCM scheme_createClientToServerMessage(SCM content) +{ + // Check that we have been provided the right Scheme type: + if (scm_string_p(content)) + { + // Convert the Scheme string to a C string: + char * contentString = scm_to_locale_stringn(content, NULL); + + // Create the message: + struct ClientToServerMessage * message = createClientToServerMessage(contentString); + + // Free the converted string: + free(contentString); + + // Return the pointer as a Scheme object: + return scm_from_pointer(message, NULL); + } + else + { + return SCM_BOOL_F; + } +} + +// Allocate and initialize a server to client message using the passed values: +struct ServerToClientMessage * createServerToClientMessage(uint8_t type, char * name, char * content) +{ + // Allocate the needed memory for the message: + struct ServerToClientMessage * newMessage = calloc(1, sizeof(struct ServerToClientMessage)); + + // Copy the type: + newMessage->type = type; + + // Copy the strings and terminate them: + strncpy(newMessage->name, name, MESSAGE_NAME_LENGTH - 1); + newMessage->name[MESSAGE_NAME_LENGTH - 1] = '\0'; + strncpy(newMessage->content, content, MESSAGE_CONTENT_LENGTH - 1); + newMessage->content[MESSAGE_CONTENT_LENGTH - 1] = '\0'; + + return newMessage; +} + +// A Scheme wrapper for creating server to client messages: +SCM scheme_createServerToClientMessage(SCM type, SCM name, SCM content) +{ + // Check that we have been provided the right Scheme type: + if (scm_integer_p(type) && scm_string_p(name) && scm_string_p(content)) + { + // Convert the integer to a C integer: + uint8_t typeInteger = scm_to_uint8(type); + + // Convert the Scheme strings to C strings: + char * nameString = scm_to_locale_stringn(name, NULL); + char * contentString = scm_to_locale_stringn(content, NULL); + + // Create the message: + struct ServerToClientMessage * message = + createServerToClientMessage(typeInteger, nameString, contentString); + + // Free the converted string: + free(nameString); + free(contentString); + + // Return the pointer as a Scheme object: + return scm_from_pointer(message, NULL); + } + else + { + return SCM_BOOL_F; + } +} + +// ================================================ +// | End of messages.c, copyright notice follows. | +// ================================================ + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. + +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . diff --git a/source/messages.h b/source/messages.h new file mode 100644 index 0000000..c5c0649 --- /dev/null +++ b/source/messages.h @@ -0,0 +1,58 @@ +// ========================================= +// | SilverMUD - messages.h | +// | Copyright (C) 2023, Barra Ó Catháin | +// | See end of file for copyright notice. | +// ========================================= +#ifndef MESSAGES_H +#define MESSAGES_H +#include +#define MESSAGE_NAME_LENGTH 128 +#define MESSAGE_CONTENT_LENGTH 1024 + +enum MessageTypes +{ + SYSTEM, + CLIENT_SETTING, + COMMAND_OUTPUT, + LOCAL_CHAT, + PLAYER_CHAT, + PARTY_CHAT +}; + +struct ClientToServerMessage +{ + char content[MESSAGE_CONTENT_LENGTH]; +}; + +struct ServerToClientMessage +{ + uint8_t type; + char name[MESSAGE_NAME_LENGTH]; + char content[MESSAGE_CONTENT_LENGTH]; +}; + +// Allocate and initialize a client to server message using the passed values: +struct ClientToServerMessage * createClientToServerMessage(char * content); +SCM scheme_createClientToServerMessage(SCM content); + + // Allocate and initialize a server to client message using the passed values: +struct ServerToClientMessage * createServerToClientMessage(uint8_t type, char * name, char * content); +SCM scheme_createServerToClientMessage(SCM type, SCM name, SCM content); + +#endif +// ================================================ +// | End of messages.h, copyright notice follows. | +// ================================================ + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. + +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . diff --git a/source/server/.placeholder b/source/server/.placeholder deleted file mode 100644 index e69de29..0000000 diff --git a/source/server/connections.c b/source/server/connections.c new file mode 100644 index 0000000..a49759f --- /dev/null +++ b/source/server/connections.c @@ -0,0 +1,205 @@ +// ========================================= +// | SilverMUD Server - connections.c | +// | Copyright (C) 2023, Barra Ó Catháin | +// | See end of file for copyright notice. | +// ========================================= +#include +#include + +#include "connections.h" + +static struct ClientConnectionNode * findMiddle(struct ClientConnectionNode * start, struct ClientConnectionNode * end) +{ + while (start != end) + { + start = start->next; + if(start == end) + { + return start; + } + end = end->previous; + } + + return start; +} + +struct ClientConnection * findConnectionByFileDescriptor(struct ClientConnectionList * list, int fileDescriptor) +{ + struct ClientConnectionNode * start = list->head, * end = list->tail, * middle = findMiddle(start, end); + while (start != end) + { + if (middle->connection->fileDescriptor == fileDescriptor) + { + return middle->connection; + } + else if (middle->connection->fileDescriptor > fileDescriptor) + { + end = middle->previous; + middle = findMiddle(start, end); + } + else + { + start = middle->next; + middle = findMiddle(start, end); + } + } + if (start->connection->fileDescriptor == fileDescriptor) + { + return start->connection; + } + else + { + return NULL; + } +} + +struct ClientConnection * findConnectionByTlsSession(struct ClientConnectionList * list, gnutls_session_t * tlsSession) +{ + +} + +int removeConnectionByFileDescriptor(struct ClientConnectionList * list, int fileDescriptor) +{ + struct ClientConnectionNode * start = list->head, * end = list->tail, * middle = findMiddle(start, end), * toDelete = NULL; + + // Find the node that is to be deleted: + while (start != end && toDelete == NULL) + { + if (middle->connection->fileDescriptor == fileDescriptor) + { + toDelete = middle; + } + else if (middle->connection->fileDescriptor > fileDescriptor) + { + end = middle->previous; + middle = findMiddle(start, end); + } + else + { + start = middle->next; + middle = findMiddle(start, end); + } + } + if (start->connection->fileDescriptor == fileDescriptor) + { + toDelete = start; + } + + if (toDelete == NULL) + { + return -1; + } + + // Set the appropriate pointers on other nodes: + if (toDelete->previous != NULL) + { + toDelete->previous->next = toDelete->next; + } + if (toDelete->next != NULL) + { + toDelete->next->previous = toDelete->previous; + } + + // Set the appropriate pointers on the list: + if (list->head == toDelete) + { + list->head = toDelete->next; + } + if (list->tail == toDelete) + { + list->tail = toDelete->previous; + } + + + list->clientCount--; + + // Free the connection: + free(toDelete->connection->tlsSession); + free(toDelete->connection); + free(toDelete); + + return 0; +} + +struct ClientConnection * addNewConnection(struct ClientConnectionList * list, int fileDescriptor, gnutls_session_t * tlsSession) +{ + // Allocate memory for the structures: + struct ClientConnectionNode * newConnectionNode = calloc(1, sizeof(struct ClientConnectionNode)); + newConnectionNode->connection = calloc(1, sizeof(struct ClientConnection)); + + // Set the appropriate data in the structure: + newConnectionNode->next = NULL; + newConnectionNode->previous = NULL; + newConnectionNode->connection->tlsSession = tlsSession; + newConnectionNode->connection->fileDescriptor = fileDescriptor; + + // If it's the first node in the list: + if (list->head == NULL && list->tail == NULL) + { + list->head = newConnectionNode; + list->tail = newConnectionNode; + + list->clientCount++; + + return newConnectionNode->connection; + } + + // Insert it in the appropriate place in the list: + else + { + struct ClientConnectionNode * currentNode = list->head; + + // Seek through the list until we find the appropriate spot to insert the new connection: + while (currentNode->connection->fileDescriptor < fileDescriptor) + { + // If we've reached the end of the list: + if (currentNode->next == NULL) + { + currentNode->next = newConnectionNode; + newConnectionNode->previous = currentNode; + list->tail = newConnectionNode; + + list->clientCount++; + + return newConnectionNode->connection; + } + else + { + currentNode = currentNode->next; + } + } + + newConnectionNode->previous = currentNode->previous; + newConnectionNode->next = currentNode; + currentNode->previous = newConnectionNode; + + if (newConnectionNode->previous == NULL) + { + list->head = newConnectionNode; + } + if (newConnectionNode->next == NULL) + { + list->tail = newConnectionNode; + } + list->clientCount++; + + return newConnectionNode->connection; + } +} + +// =================================================== +// | End of connections.c, copyright notice follows. | +// =================================================== + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. + +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . diff --git a/source/server/connections.h b/source/server/connections.h new file mode 100644 index 0000000..4ba77c9 --- /dev/null +++ b/source/server/connections.h @@ -0,0 +1,56 @@ +// ========================================= +// | SilverMUD Server - connections.h | +// | Copyright (C) 2023, Barra Ó Catháin | +// | See end of file for copyright notice. | +// ========================================= +#ifndef CONNECTIONS_H +#define CONNECTIONS_H +#include +#include + +struct ClientConnection +{ + gnutls_session_t * tlsSession; + struct Player * player; + int fileDescriptor; +}; + +struct ClientConnectionNode +{ + struct ClientConnection * connection; + struct ClientConnectionNode * next; + struct ClientConnectionNode * previous; +}; + +struct ClientConnectionList +{ + size_t clientCount; + struct ClientConnectionNode * head; + struct ClientConnectionNode * tail; +}; + + +//struct ClientConnection * findConnectionByPlayer(struct ClientConnectionList * list); +struct ClientConnection * findConnectionByFileDescriptor(struct ClientConnectionList * list, int fileDescriptor); +struct ClientConnection * findConnectionByTlsSession(struct ClientConnectionList * list, gnutls_session_t * tlsSession); + +int removeConnectionByFileDescriptor(struct ClientConnectionList * list, int fileDescriptor); +struct ClientConnection * addNewConnection(struct ClientConnectionList * list, int fileDescriptor, gnutls_session_t * tlsSession); + +#endif +// =================================================== +// | End of connections.h, copyright notice follows. | +// =================================================== + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. + +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . diff --git a/source/server/main.c b/source/server/main.c new file mode 100644 index 0000000..a7bab43 --- /dev/null +++ b/source/server/main.c @@ -0,0 +1,273 @@ +// ========================================= +// | SilverMUD Server - main.c | +// | Copyright (C) 2023, Barra Ó Catháin | +// | See end of file for copyright notice. | +// ========================================= +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "player-data.h" +#include "connections.h" +#include "../messages.h" +#include "scheme-integration.h" + +static const int PORT = 5000; +static const int CONCURRENT_PLAYER_COUNT = 256; + +int main (int argc, char ** argv) +{ + // Print a welcome message: + printf("SilverMUD Server - Starting Now.\n" + "================================\n"); + + // Initialize Scheme: + scm_init_guile(); + + // Start the REPL server on a UNIX socket: + scm_c_eval_string("(begin (use-modules (system repl server))" + "(if (file-exists? \"silvermud-repl\") (delete-file \"silvermud-repl\"))" + "(spawn-server (make-unix-domain-server-socket #:path \"silvermud-repl\")))"); + + // Create a socket to listen for connections on: + int masterSocket = socket(AF_INET, SOCK_STREAM, 0); + if (masterSocket < 0) + { + fprintf(stderr, "Failed to create socket. Aborting.\n"); + exit(EXIT_FAILURE); + } + + // Allow reusing the address so that quick re-launching doesn't fail: + setsockopt(masterSocket, SOL_SOCKET, SO_REUSEADDR, &(int){1}, sizeof(int)); + setsockopt(masterSocket, SOL_SOCKET, SO_REUSEPORT, &(int){1}, sizeof(int)); + + // Setup the server address struct to bind the master socket to: + struct sockaddr_in serverAddress; + memset(&serverAddress, 0, sizeof(struct sockaddr_in)); + + // Assign the IP address and port to the server address struct: + serverAddress.sin_family = AF_INET; + serverAddress.sin_addr.s_addr = htonl(INADDR_ANY); + serverAddress.sin_port = htons(PORT); + + // Bind the master socket to the server address: + if ((bind(masterSocket, (struct sockaddr *)&serverAddress, sizeof(struct sockaddr_in))) != 0) + { + fprintf(stderr, "Failed to bind socket. Aborting.\n"); + exit(EXIT_FAILURE); + } + + // Begin listening: + if ((listen(masterSocket, CONCURRENT_PLAYER_COUNT)) != 0) + { + fprintf(stderr, "Failed to begin listening on the master socket. Aborting.\n"); + exit(EXIT_FAILURE); + } + + // Create an epoll instance for managing connections, and add the master socket to it: + int connectedClients = epoll_create(CONCURRENT_PLAYER_COUNT); + if (connectedClients < 0) + { + fprintf(stderr, "Failed to create epoll instance. Aborting.\n"); + exit(EXIT_FAILURE); + } + + // Setup the epoll events we want to watch for: + struct epoll_event watchedEvents; + watchedEvents.events = EPOLLIN; + watchedEvents.data.fd = masterSocket; + epoll_ctl(connectedClients, EPOLL_CTL_ADD, masterSocket, &watchedEvents); + + int eventsCount = 0; + struct epoll_event events[1024]; + + // Setup the needed anonymous certificate for TLS: + gnutls_global_init(); + gnutls_anon_server_credentials_t serverKey; + gnutls_anon_allocate_server_credentials(&serverKey); + gnutls_anon_set_server_known_dh_params(serverKey, GNUTLS_SEC_PARAM_MEDIUM); + + // Create a client connection list to allow us to associate TLS sessions and sockets and players: + struct ClientConnectionList clientConnections; + + // Create some structures needed to store global state: + struct PlayerList * globalPlayerList = createPlayerList(); + + // Start a REPL thread: + //pthread_t schemeREPLThread; + //pthread_create(&schemeREPLThread, NULL, schemeREPLHandler, NULL); + + while (true) + { + do + { + eventsCount = epoll_wait(connectedClients, events, 1024, -1); + } while (eventsCount < 0 && errno == EINTR); + + if (eventsCount == -1) + { + fprintf(stderr, "epoll_wait() failed. Aborting.\n"); + exit(EXIT_FAILURE); + } + + for (int index = 0; index < eventsCount; index++) + { + // If it's the master socket, it's a new client connecting: + if (events[index].data.fd == masterSocket) + { + // Setup a TLS Session: + gnutls_session_t * tlsSession = calloc(1, sizeof(gnutls_session_t)); + gnutls_init(tlsSession, GNUTLS_SERVER); + gnutls_priority_set_direct(*tlsSession, "NORMAL:+ANON-ECDH:+ANON-DH", NULL); + gnutls_credentials_set(*tlsSession, GNUTLS_CRD_ANON, serverKey); + gnutls_handshake_set_timeout(*tlsSession, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT); + + // Accept the connection: + int newSocket = accept(masterSocket, NULL, NULL); + gnutls_transport_set_int(*tlsSession, newSocket); + + // Perform a TLS handshake: + int handshakeReturnValue = 0; + do + { + handshakeReturnValue = gnutls_handshake(*tlsSession); + } while (handshakeReturnValue < 0 && gnutls_error_is_fatal(handshakeReturnValue) == 0); + + // If the handshake was unsuccessful, close the connection: + if (handshakeReturnValue < 0) + { + printf("%d", handshakeReturnValue); + fflush(stdout); + gnutls_bye(*tlsSession, 2); + shutdown(newSocket, 2); + close(newSocket); + break; + } + + // Setup the epoll events we want to watch for: + watchedEvents.events = EPOLLIN; + watchedEvents.data.fd = newSocket; + epoll_ctl(connectedClients, EPOLL_CTL_ADD, newSocket, &watchedEvents); + + // Add the connection to the list: + struct ClientConnection * newConnection = addNewConnection(&clientConnections, newSocket, tlsSession); + newConnection->player = createNewPlayer(newConnection); + sprintf(newConnection->player->name, "Player %02d", globalPlayerList->count + 1); + addToPlayerList(newConnection->player, globalPlayerList); + + // Prepare a welcome message: + struct ServerToClientMessage welcomeMessage; + welcomeMessage.type = SYSTEM; + sprintf(welcomeMessage.content, + (clientConnections.clientCount > 1) ? + "Welcome to the server. There are %d players connected." : + "Welcome to the server. There is %d player connected.", + clientConnections.clientCount); + + // Send the welcome message: + gnutls_record_send(*tlsSession, &welcomeMessage, sizeof(struct ServerToClientMessage)); + + // Report the new connection on the server: + printf("New connection established. %d client(s), session ID %u.\n", + clientConnections.clientCount, tlsSession); + } + else + { + // Find the corresponding TLS session: + struct ClientConnection * connection = findConnectionByFileDescriptor(&clientConnections, events[index].data.fd); + if (connection != NULL) + { + // Read the data from the TLS session: + struct ClientToServerMessage message; + int returnValue = gnutls_record_recv(*connection->tlsSession, &message, sizeof(struct ClientToServerMessage)); + + if (returnValue == 0 || returnValue == -10) + { + epoll_ctl(connectedClients, EPOLL_CTL_DEL, connection->fileDescriptor, &watchedEvents); + shutdown(connection->fileDescriptor, 2); + close(connection->fileDescriptor); + removeFromPlayerList(connection->player, globalPlayerList); + deallocatePlayer(&connection->player); + removeConnectionByFileDescriptor(&clientConnections, connection->fileDescriptor); + continue; + } + else if (returnValue == sizeof(struct ClientToServerMessage)) + { + // TODO: BEGIN ACTUAL COMMAND INTERPRETER + // ONLY FOR DEMO + if (strncmp(message.content, "NAME ", 5) == 0 && message.content[5] != '\0') + { + strncpy(connection->player->name, &message.content[5], 64); + continue; + } + // ONLY FOR DEMO + + struct ServerToClientMessage outputMessage; + + // Copy the message to the output format: + outputMessage.type = LOCAL_CHAT; + + strncpy(outputMessage.name, connection->player->name, 64); + strncpy(outputMessage.content, message.content, MESSAGE_CONTENT_LENGTH); + + // Echo the message into all other clients: (Temporary) + struct ClientConnectionNode * currentClient = clientConnections.head; + while (currentClient != NULL) + { + gnutls_record_send(*currentClient->connection->tlsSession, &outputMessage, + sizeof(struct ServerToClientMessage)); + currentClient = currentClient->next; + } + } + } + else + { + printf("Didn't find associated TLS Session!\n"); + fflush(stdout); + // Remove the file descriptor from our watched set and close it: + epoll_ctl(connectedClients, EPOLL_CTL_DEL, events[index].data.fd, &watchedEvents); + close(events[index].data.fd); + removeConnectionByFileDescriptor(&clientConnections, events[index].data.fd); + } + } + } + } + + // Wait for all other threads to terminate: + //pthread_join(schemeREPLThread, NULL); + + // Deallocate the global state structures: + deallocatePlayerList(&globalPlayerList); + + // Return a successful status code to the operating system: + return 0; +} + +// ============================================ +// | End of main.c, copyright notice follows. | +// ============================================ + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. + +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . diff --git a/source/server/player-data.c b/source/server/player-data.c new file mode 100644 index 0000000..c3d530d --- /dev/null +++ b/source/server/player-data.c @@ -0,0 +1,250 @@ +// ========================================= +// | SilverMUD Server - player-data.c | +// | Copyright (C) 2023, Barra Ó Catháin | +// | See end of file for copyright notice. | +// ========================================= +#include +#include +#include +#include "player-data.h" + +// Internal Functions: +// =================== +static struct PlayerListNode * findMiddle(struct PlayerListNode * start, struct PlayerListNode * end) +{ + while (start != end) + { + start = start->next; + if(start == end) + { + return start; + } + end = end->previous; + } + + return start; +} +// ==================== + +// Allocates and sets up a new player according to the world's starter character sheet: +struct Player * createNewPlayer(struct ClientConnection * connection) +{ + struct Player * newPlayer = calloc(1, sizeof(struct Player)); + newPlayer->connection = connection; + + return newPlayer; +} + +// Deallocates a player: +void deallocatePlayer(struct Player ** player) +{ + free(*player); + *player = NULL; +} + +struct PlayerList * createPlayerList() +{ + struct PlayerList * newPlayerList = calloc(1, sizeof(struct PlayerList)); + newPlayerList->count = 0; + newPlayerList->head = NULL; + newPlayerList->tail = NULL; + + return newPlayerList; +} + +void deallocatePlayerList(struct PlayerList ** playerList) +{ + struct PlayerListNode * node = (*playerList)->head, * nextNode; + + // Deallocate all nodes in the list: + while (node != NULL) + { + nextNode = node->next; + free(node); + node = nextNode; + } + + // Deallocate the list itself: + free(*playerList); + + // Set the pointer to null: + playerList = NULL; +} + +int addToPlayerList(struct Player * player, struct PlayerList * playerList) +{ + // Check that the player isn't already in the list: + if (isInPlayerList(player, playerList)) + { + return playerList->count; + } + else + { + // Create a node to add to the list: + struct PlayerListNode * newNode = calloc(1, sizeof(struct PlayerListNode)); + newNode->player = player; + newNode->next = NULL; + newNode->previous = NULL; + + // Find the position that the new node is to go into: + + // If the list is empty: + if (playerList->count == 0) + { + playerList->head = newNode; + playerList->tail = newNode; + playerList->count = 1; + return playerList->count; + } + + struct PlayerListNode * currentNode = playerList->head; + while (strncmp(player->name, currentNode->player->name, 64) < 0) + { + // If we reach the end of the list: + if (currentNode->next == NULL) + { + currentNode->next = newNode; + newNode->previous = currentNode; + playerList->tail = newNode; + playerList->count++; + + return playerList->count; + } + + else + { + currentNode = currentNode->next; + } + } + + // Set the appropriate pointers in the new node: + newNode->previous = currentNode->previous; + currentNode->previous = newNode; + newNode->next = currentNode; + + // Set the proper pointers if we're at the ends of the list: + if (newNode->previous == NULL) + { + playerList->head = newNode; + } + if (newNode->next == NULL) + { + playerList->tail = newNode; + } + + playerList->count++; + + return playerList->count; + } +} + +int removeFromPlayerList(struct Player * player, struct PlayerList * playerList) +{ + struct PlayerListNode * currentNode = playerList->head; + while (currentNode != NULL) + { + if (currentNode->player == player) + { + // Adjust the proper pointers: + if (currentNode->previous) + { + currentNode->previous->next = currentNode->next; + } + if (currentNode->next) + { + currentNode->next->previous = currentNode->previous; + } + + // Handle the special case of the head and tail of the list: + if (playerList->head == currentNode) + { + playerList->head == playerList->head->next; + } + if (playerList->tail == currentNode) + { + playerList->tail == playerList->tail->previous; + } + + // Handle the special case of an empty list: + if (playerList->count - 1 == 0) + { + playerList->head = NULL; + playerList->tail = NULL; + } + + // Delete the node: + free(currentNode); + + return --(playerList->count); + } + currentNode = currentNode->next; + } +} + + +// Returns the Player with the given name from a PlayerList, or NULL otherwise: +struct Player * getFromPlayerList(char * playerName, struct PlayerList * playerList) +{ + struct PlayerListNode * start = playerList->head, * end = playerList->tail, * middle = findMiddle(start, end); + int returnValue = 0; + + while (start != end) + { + returnValue = strncmp(middle->player->name, playerName, 64); + + if (returnValue < 0) + { + start = middle->next; + middle = findMiddle(start, end); + } + else if (returnValue > 0) + { + end = middle->next; + middle = findMiddle(start, end); + } + else if (returnValue == 0) + { + return middle->player; + } + } + if (strncmp(start->player->name, playerName, 64) == 0) + { + return start->player; + } + else + { + return NULL; + } +} + +// Returns true if the given Player is in the given PlayerList: +bool isInPlayerList(struct Player * player, struct PlayerList * playerList) +{ + struct PlayerListNode * currentNode = playerList->head; + while (currentNode != NULL) + { + if (currentNode->player == player) + { + return true; + } + currentNode = currentNode->next; + } + return false; +} + +// =================================================== +// | End of player-data.c, copyright notice follows. | +// =================================================== + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. + +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . diff --git a/source/server/player-data.h b/source/server/player-data.h new file mode 100644 index 0000000..d908e5e --- /dev/null +++ b/source/server/player-data.h @@ -0,0 +1,79 @@ +// ========================================= +// | SilverMUD Server - player-data.h | +// | Copyright (C) 2023, Barra Ó Catháin | +// | See end of file for copyright notice. | +// ========================================= +#ifndef PLAYER_DATA_H +#define PLAYER_DATA_H +#include +#include "connections.h" + +// ================================================================= +// Players - A structure for representing a single player character: +// ================================================================= +struct Player +{ + struct ClientConnection * connection; + char name[64]; +}; + +// Functions: +// ========== + +// Allocates and sets up a new player according to the world's starter character sheet: +struct Player * createNewPlayer(struct ClientConnection * connection); + +// Deallocates a player: +void deallocatePlayer(struct Player ** player); + +// ======================================================================================== +// Player Lists - A structure for managing a collection of players in a doubly linked list: +// ======================================================================================== +struct PlayerListNode +{ + struct Player * player; + struct PlayerListNode * next, * previous; +}; + +struct PlayerList +{ + size_t count; + struct PlayerListNode * head, * tail; +}; + +// Functions: +// ========== + +struct PlayerList * createPlayerList(); +void deallocatePlayerList(struct PlayerList ** playerList); + +// Adds a Player into a PlayerList, in a sorted position by character name. +// Returns the count of players in the list: +int addToPlayerList(struct Player * player, struct PlayerList * playerList); + +// Remove a Player from a PlayerList. Returns the count of players in the list: +int removeFromPlayerList(struct Player * player, struct PlayerList * playerList); + +// Returns the Player with the given name from a PlayerList, or NULL otherwise: +struct Player * getFromPlayerList(char * playerName, struct PlayerList * playerList); + +// Returns true if the given Player is in the given PlayerList: +bool isInPlayerList(struct Player * player, struct PlayerList * playerList); + +#endif +// =================================================== +// | End of player-data.h, copyright notice follows. | +// =================================================== + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. + +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . diff --git a/source/server/scheme-integration.c b/source/server/scheme-integration.c new file mode 100644 index 0000000..56bd86d --- /dev/null +++ b/source/server/scheme-integration.c @@ -0,0 +1,25 @@ +// =========================================== +// | SilverMUD Server - scheme-integration.c | +// | Copyright (C) 2023, Barra Ó Catháin | +// | See end of file for copyright notice. | +// =========================================== +#include + +SCM scheme_get_player_by_name(SCM name); + +// ========================================================== +// | End of scheme-integration.c, copyright notice follows. | +// ========================================================== + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. + +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . diff --git a/source/server/scheme-integration.h b/source/server/scheme-integration.h new file mode 100644 index 0000000..9992bfb --- /dev/null +++ b/source/server/scheme-integration.h @@ -0,0 +1,27 @@ +// =========================================== +// | SilverMUD Server - scheme-integration.h | +// | Copyright (C) 2023, Barra Ó Catháin | +// | See end of file for copyright notice. | +// =========================================== +#ifndef SCHEME_INTEGRATION_H +#define SCHEME_INTEGRATION_H + +SCM scheme_get_player_by_name(SCM name); + +#endif +// ========================================================== +// | End of scheme-integration.h, copyright notice follows. | +// ========================================================== + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. + +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see .