Version 0.0.1 of the rewritten client and server.
- Server is capable of accepting an arbitrary number of TLS encrypted client connections. - Server relays messages from one client to the others. - Server spawns a Scheme REPL available over a Unix socket. - Client is a two-pane ncurses-based client, with an input area, chat log, and game status log. - Temporary NAME command exists to change names of players. Squashed commit of the following: commit442a9319e8
Author: Barry Kane <barra@ocathain.ie> Date: Mon Oct 30 16:57:15 2023 +0000 Removed unneeded check, added temporary name command commita66a07c897
Author: Barry Kane <barra@ocathain.ie> Date: Sun Oct 29 20:15:41 2023 +0000 Properly remove disconnected players commit81fc72a1d7
Author: Barry Kane <barra@ocathain.ie> Date: Sun Oct 29 17:20:14 2023 +0000 Added system messages to client and added welcome message from server. commita1b1b80449
Author: Barra Ó Catháin <barra@ocathain.ie> Date: Tue Sep 12 23:25:44 2023 +0100 Removed now unneeded placeholders, added stubs for "rulebooks" in documentation. commit54b613befe
Author: Barra Ó Catháin <barra@ocathain.ie> Date: Tue Sep 12 22:32:19 2023 +0100 Basic implementation of player lists and tying connections to players commit5a53e9f197
Author: Barra Ó Catháin <barra@ocathain.ie> Date: Sun Sep 10 17:24:46 2023 +0100 Added basic player type containing a name, and made the server echo messages with player name. commit3fc75ef30f
Author: Barry Kane <barra@ocathain.ie> Date: Thu Aug 31 01:44:17 2023 +0100 Basic message receiver, server now echoes messages to all clients. commitb292966588
Author: Barry Kane <barra@ocathain.ie> Date: Mon Aug 28 02:53:31 2023 +0100 Fixed window height calculations. commit50dcddfc56
Author: Barry Kane <barra@ocathain.ie> Date: Mon Aug 28 02:29:21 2023 +0100 Initial ncurses setup, and layout of client. commitc043da64a2
Author: Barry Kane <barra@ocathain.ie> Date: Sat Aug 26 00:48:28 2023 +0100 Modify server and client to begin using ClientToServer messages. commit0104a11a7e
Author: Barry Kane <barra@ocathain.ie> Date: Fri Aug 25 00:34:05 2023 +0100 Added basic client capable of connecting to the server. commit080e46fe99
Author: Barry Kane <barra@ocathain.ie> Date: Thu Aug 24 00:12:27 2023 +0100 Set up GNU Autotools as build system. commit0814e437cd
Author: Barry Kane <barra@ocathain.ie> Date: Tue Aug 22 02:02:29 2023 +0100 Basic connection handling (using previous version of client) commit9801be3622
Author: Barry Kane <barra@ocathain.ie> Date: Sat Aug 19 16:00:57 2023 +0100 Renamed src back to source, because I liked it better commite2ef744e87
Author: Barry Kane <barra@ocathain.ie> Date: Sat Aug 19 00:18:03 2023 +0100 Moved scheme initialization to main thread, added basic networking The server can now listen on a port and send data to a client. commit8b0920c35d
Author: Barry Kane <barra@ocathain.ie> Date: Fri Aug 18 00:45:24 2023 +0100 Added basic implemantation of message structures. commit6ed532c368
Author: Barry Kane <barra@ocathain.ie> Date: Thu Aug 17 00:21:20 2023 +0100 Added structure section. commit48f0858735
Author: Barry Kane <barra@ocathain.ie> Date: Thu Aug 17 00:14:40 2023 +0100 Began implementation planning document. commit32503cdbca
Author: Barry Kane <barra@ocathain.ie> Date: Mon Aug 14 03:15:37 2023 +0100 Rename source/ to src/ for Autotools. commit2b488477f5
Author: Barry Kane <barra@ocathain.ie> Date: Mon Aug 14 02:46:43 2023 +0100 Added initial stubs for server. - Server now launches a thread to initialize Scheme, and drops into a REPL.
This commit is contained in:
parent
3189312340
commit
8044dd36be
|
@ -49,4 +49,63 @@
|
||||||
modules.order
|
modules.order
|
||||||
Module.symvers
|
Module.symvers
|
||||||
Mkfile.old
|
Mkfile.old
|
||||||
dkms.conf
|
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
|
|
@ -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
|
|
@ -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
|
|
@ -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:
|
|
@ -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:
|
|
@ -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:
|
||||||
|
|
|
@ -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.
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
|
@ -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 <ncurses.h>
|
||||||
|
|
||||||
|
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 <https://www.gnu.org/licenses/>.
|
|
@ -0,0 +1,145 @@
|
||||||
|
// =========================================
|
||||||
|
// | SilverMUD Client - main.c |
|
||||||
|
// | Copyright (C) 2023, Barra Ó Catháin |
|
||||||
|
// | See end of file for copyright notice. |
|
||||||
|
// =========================================
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <ncurses.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <gnutls/gnutls.h>
|
||||||
|
|
||||||
|
#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 <https://www.gnu.org/licenses/>.
|
|
@ -0,0 +1,87 @@
|
||||||
|
// ==========================================
|
||||||
|
// | SilverMUD Client - receiving-thread.c |
|
||||||
|
// | Copyright (C) 2023, Barra Ó Catháin |
|
||||||
|
// | See end of file for copyright notice. |
|
||||||
|
// ==========================================
|
||||||
|
#include <ncurses.h>
|
||||||
|
#include <gnutls/gnutls.h>
|
||||||
|
|
||||||
|
#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 <https://www.gnu.org/licenses/>.
|
|
@ -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 <ncurses.h>
|
||||||
|
#include <gnutls/gnutls.h>
|
||||||
|
|
||||||
|
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 <https://www.gnu.org/licenses/>.
|
|
@ -0,0 +1,113 @@
|
||||||
|
// =========================================
|
||||||
|
// | SilverMUD - messages.c |
|
||||||
|
// | Copyright (C) 2023, Barra Ó Catháin |
|
||||||
|
// | See end of file for copyright notice. |
|
||||||
|
// =========================================
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <libguile.h>
|
||||||
|
|
||||||
|
#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 <https://www.gnu.org/licenses/>.
|
|
@ -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 <libguile.h>
|
||||||
|
#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 <https://www.gnu.org/licenses/>.
|
|
@ -0,0 +1,205 @@
|
||||||
|
// =========================================
|
||||||
|
// | SilverMUD Server - connections.c |
|
||||||
|
// | Copyright (C) 2023, Barra Ó Catháin |
|
||||||
|
// | See end of file for copyright notice. |
|
||||||
|
// =========================================
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <gnutls/gnutls.h>
|
||||||
|
|
||||||
|
#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 <https://www.gnu.org/licenses/>.
|
|
@ -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 <stddef.h>
|
||||||
|
#include <gnutls/gnutls.h>
|
||||||
|
|
||||||
|
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 <https://www.gnu.org/licenses/>.
|
|
@ -0,0 +1,273 @@
|
||||||
|
// =========================================
|
||||||
|
// | SilverMUD Server - main.c |
|
||||||
|
// | Copyright (C) 2023, Barra Ó Catháin |
|
||||||
|
// | See end of file for copyright notice. |
|
||||||
|
// =========================================
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <libguile.h>
|
||||||
|
#include <sys/epoll.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <gnutls/gnutls.h>
|
||||||
|
|
||||||
|
#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 <https://www.gnu.org/licenses/>.
|
|
@ -0,0 +1,250 @@
|
||||||
|
// =========================================
|
||||||
|
// | SilverMUD Server - player-data.c |
|
||||||
|
// | Copyright (C) 2023, Barra Ó Catháin |
|
||||||
|
// | See end of file for copyright notice. |
|
||||||
|
// =========================================
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#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 <https://www.gnu.org/licenses/>.
|
|
@ -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 <stdbool.h>
|
||||||
|
#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 <https://www.gnu.org/licenses/>.
|
|
@ -0,0 +1,25 @@
|
||||||
|
// ===========================================
|
||||||
|
// | SilverMUD Server - scheme-integration.c |
|
||||||
|
// | Copyright (C) 2023, Barra Ó Catháin |
|
||||||
|
// | See end of file for copyright notice. |
|
||||||
|
// ===========================================
|
||||||
|
#include <libguile.h>
|
||||||
|
|
||||||
|
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 <https://www.gnu.org/licenses/>.
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
Loading…
Reference in New Issue