ZX Spectrum Multi User Dungeon (MUD)

Propose new game/software design concepts or new game/software ideas. They can be as whimsical as you like, just be careful you don't ask someone to make it for you...
Post Reply
User avatar
MonkZy
Manic Miner
Posts: 278
Joined: Thu Feb 08, 2018 1:01 pm

ZX Spectrum Multi User Dungeon (MUD)

Post by MonkZy »

Many years ago, I was fortunate to be at a school with multiple networked BBC Masters. The machines were networked using Econet, there was also a file server connected to a Winchester HDD (30MB if my memory serves me well). This allowed us to play a game called 'The Cave'. The game was a multi user dungeon, a text adventure with multiple users. Some hours were spent mucking about roaming the cave, bashing teachers/1st years/noobs. Much fun was had. This was in fact my very first experience of online gaming.

Fast forward to around 1998 when I finally had a modem and access to the internet. By this time MUD's had come a long way. Class based adventuring with huge worlds to explore and puzzles to solve. Shops, traders and currency were all now part of adventure systems. Many hours were lost leveling characters and diving deep into player generated lore.

MUD's are very different to usual text adventures. Politics and social structures play a huge part in the game, which can bring extra enjoyment to text adventuring but can also bring drama and upset. So MUD's may not be for all text adventure fans, some preferring the solo questing found on traditional text adventures.

I am tinkering with the idea of making a ZX Spectrum MUD client. MUD's are a server/client system. All of the game data and processing is done server side. The client is a simple Telnet client, a dumb terminal. On the PC many customised telnet clients have been made that give you many MUD related functions. I intend to code a telnet client that is totally geared toward MUD's.

My progress so far is purely server side. I have managed to compile a build of CircleMUD (based on DikuMUD) and have had it up and running, tested with a Linux telnet client. CircleMUD has some great resources for editing the files that create the world. It is a full feature server that has shops, mobile non-player characters, objects, combat and much more. It also has a way of editing the world from inside the game, which allows people to easily collaborate with the building of the world. So, I think I have the server side sorted, although I have plenty to learn about the admin. I have compiled the code on a RaspberryPi 2 I had spare. I can keep the server on 24/7 at little cost to me. I am a little worried about the security risk as I will have to give out my home IP and forward a port to the Pi, so maybe I could get some advice on this. I may need a VPN or some such. For now I would be fine to give my IP in DM to interested parties. Currently the server is loaded with the default world, which works just fine. I intend to alter this soon, the start location will be the Baron of Beef pub, maybe have a Sir Clive and Chris Curry mobile.

The client side will be written for Spectranet. I do not have hardware sadly, my two real speccies are not in good working order so I think spending out for a Spectranet card would be unwise at this time (this may change soon). FUSE does have excellent Spectranet support though, and I can develop with this. If interest is there I would like to make a SpecNEXT version and also a version for Russian clones and other Spectrum networking interfaces. I would need help for those.

I am posting this to create a thread to gauge interest and to ramble about the client code.

Links for more info:

CircleMUD documentation : https://www.circlemud.org/cdp/
MUD Wikipedia page : https://en.wikipedia.org/wiki/MUD

MUD's can be also used as a chatroom, similar to IRC. More fun than IRC because the emotes are more complex and object based, and you can bash each other with a wide array of blunt and sharpened instruments.

Please reply if this idea interests you, or if you have any ideas of the direction of the project.
User avatar
p13z
Manic Miner
Posts: 611
Joined: Sun Feb 17, 2019 10:41 pm
Location: UK
Contact:

Re: ZX Spectrum Multi User Dungeon (MUD)

Post by p13z »

Sounds like a perfect use for Spectranet. My Speccy and Spectranet have been packed away in the loft for a few years - but I would be keen to try this out if it, and my Speccy, see the light of day.
User avatar
MonkZy
Manic Miner
Posts: 278
Joined: Thu Feb 08, 2018 1:01 pm

Re: ZX Spectrum Multi User Dungeon (MUD)

Post by MonkZy »

[mention]p13z[/mention] Thanks for the feed back. It will take me some time to code the client, but once I get something useful to test it would be great if you can help with a proper hardware test.
User avatar
MonkZy
Manic Miner
Posts: 278
Joined: Thu Feb 08, 2018 1:01 pm

Re: ZX Spectrum Multi User Dungeon (MUD)

Post by MonkZy »

Update:

Baby steps have been taken with the coding of the client. I have successfully got the Spectrum communicating with the server. At this point the client only receives the greeting message from the MUD after sending a handshake. Not much, but I am am pretty chuffed.

Image

I have decided to use the 42 column print routine that is built into the Spectranet ROM. I was going to use a proportional spaced text, but a fixed column print mode is better for formatting text and for ASCII art. CircleMUD is geared for an 80 column terminal so I will have to make code which neatly wraps the text into a 42 column client. Some texts will have to be altered in the MUD to accommodate the 42 columns.

To Do's:

Code a scroll routine to scroll the output text.
Code the keyboard input and send text routine.
Code a socket polling routine that checks the socket for incoming text.

Decisions to be made:

How will I handle the text? Do I store a buffer of text that can be rolled back, it would be useful to be able to do this as text comes at you fast in a busy MUD? Do I simply have a 'more..' message when the incoming text goes over the 28 lines of the client screen, wait for the user to hit a key to continue and discard the text which leaves the screen?

At this point I would really like to say a huge thanks to Winston (Dylan Smith), [mention]Guesser[/mention] and all the other people involved in making the Spectranet and the excellent documentation. Also a big thanks to the authors/maintainers of the FUSE project.

Code:
Spoiler

labels.asm contains declarations of all the entry points and constants used by the Spectranet. It is an abridged version of spectranet.inc.

Code: Select all

;;; Defined Labels

; Hardware page-in entry points
PAGEIN		equ 0x3FF9
PAGEOUT		equ 0x007C
HLCALL		equ 0x3FFA
IXCALL		equ 0x3FFD

; Jump table entry points
SOCKET		equ 0x3E00	; Allocate a socket
CLOSE		equ 0x3E03	; Close a socket
LISTEN		equ 0x3E06	; Listen for incoming connections
ACCEPT		equ 0x3E09	; Accept an incoming connection
BIND		equ 0x3E0C	; Bind a local address to a socket
CONNECT		equ 0x3E0F	; Connect to a remote host
SEND		equ 0x3E12	; Send data 
RECV		equ 0x3E15	; Receive data 

POLL		equ 0x3E1E	; Poll a list of sockets
POLLALL		equ 0x3E21	; Poll all open sockets
POLLFD		equ 0x3E24	; Poll a single socket
GETHOSTBYNAME	equ 0x3E27	; Look up a hostname
PUTCHAR42	equ 0x3E2A	; 42 column print write a character
PRINT42		equ 0x3E2D	; 42 column print a null terminated string
CLEAR42		equ 0x3E30	; Clear the screen and reset 42-col print

LONG2IPSTRING	equ 0x3E39	; Convert a 4 byte big endian long to an IP
IPSTRING2LONG	equ 0x3E3C	; Convert an IP to a 4 byte big endian long
ITOA8		equ 0x3E3F	; Convert a byte to ascii
RAND16		equ 0x3E42	; 16 bit PRNG

GETKEY		equ 0x3E66	; Get a key from the keyboard, and put it in A
KEYUP		equ 0x3E69	; Wait for key release
INPUTSTRING	equ 0x3E6C	; Read a string into buffer at DE

; POLL status bits
BIT_RECV	equ 2
BIT_DISCON	equ 1
BIT_CONN	equ 0

;;; Socket Types (added by me from socket.h)

AF_INET		equ 0
SOCK_STREAM	equ 1
SOCK_DGRAM	equ 2
SOCK_RAW	equ 3

;;; System Variables (only available while paged in)

v_column	equ 0x3F00
v_row		equ 0x3F01
v_rowcount	equ 0x3F03
Client code. Basically the code from Tutorial 3 on the wiki with minor tweaks, with an additional custom CLEAR42 to place a title bar and use colours other than white on blue provided by the ROM. I am using a local IP at this point.

Code: Select all

; A simple TCP client - this goes along with the following:
; http://spectrum.alioth.net/doc/index.php/Spectranet:_Tutorial_3

        include "labels.asm"

        org 33000               ; Start with RAND USR 33000

        call CLS42		; clear the screen and set cursor

        ; Open the socket - this simply allocates the resources we
        ; need, and gives us an identifier (file descriptor) for the socket.
        ld c, SOCK_STREAM       ; We want a stream - i.e. TCP socket.
        ld hl, SOCKET           ; Routine to call in ROM - SOCKET.
        call HLCALL
        jp c, ERROR             ; Exit on error.
        ld (v_sockfd), a        ; Save the socket.

        ; Connect to the remote host. First print a message saying
        ; that this is what we're trying.
	ld ix, PRINT42
        ld hl, STR_connect      
        call IXCALL

        ; ...now connect
        ld a, (v_sockfd)        ; Get the socket file descriptor
        ld de, ip_buffer        ; DE = the address of the IP address in memory
        ld bc, 4000		; we want to connect to port 4000
        ld hl, CONNECT          ; call the CONNECT routine
        call HLCALL
        jp c, ERROR             ; carry is set on error.

        ; Now send some data - 'ping' which will elicit a response.
        ld a, (v_sockfd)        ; Get the socket file descriptor
        ld de, STR_send         ; String to send
        ld bc, 4		; which is 18 bytes long
        ld hl, SEND             ; to be passed to the ROM SEND routine
        call HLCALL
        jr c, ERROR

        ; Get the response and print it to the screen. Display a message
        ; saying we're waiting.
	ld ix, PRINT42
        ld hl, STR_receiving
        call IXCALL

        ld a, (v_sockfd)
        ld de, resp_buffer      ; the buffer we want to fill.
        ld bc, 1024             ; up to 1K at a time
        ld hl, RECV             ; and use the ROM's RECV routine
        call HLCALL
        jr c, ERROR
        
        ; Make sure the returned buffer has a null on the end for
        ; when we go to print it.
        ld hl, resp_buffer
        add hl, bc              ; BC contains the number of bytes received.
        ld (hl), 0              ; put a NULL on the end

	ld ix, PRINT42
        ld hl, resp_buffer      ; and print the data we received
        call IXCALL

        ld a, (v_sockfd)        ; Get the socket fd
        ld hl, CLOSE            ; and close it.
        call HLCALL

	ld ix, PRINT42
        ld hl, STR_done         ; Print 'Done'
        call IXCALL
        ret

; The next two routines are support routines - exit on error, and print
; a string.
ERROR
	ld ix, PRINT42
        ld hl, STR_error        ; Error string
        call IXCALL              ; print it
        ld a, (v_sockfd)
        and a                   ; is the socket set?
        ret z                   ; no.. but if it is
        ld hl, CLOSE            ; clean up the socket
        call HLCALL
        ret        

;; Clear the screen, set PRINT42 cursor and set the title bar.
CLS42
	ld a, 7
	out (254), a		; white border
	ld hl, 16384
	ld de, 16385
	ld bc, 6144
	ld (hl), l
	ldir
	ld (hl), 71		; bright 1/black paper/white ink
	ld bc, 32
	ldir
	ld (hl), 56		; white paper/black ink
	ld bc, 735
	ldir
	call PAGEIN
	ld a,9
	ld (v_column), a
	xor a
	ld (v_rowcount), a
	ld hl, 16384
	ld (v_row), hl 
        ld hl, STR_titlebar	; Title bar string
        call PRINT42		; print it
	call PAGEOUT
	ld hl, 16385
	ld de, stripes

	ld b,8
loop1
	push bc
	push hl
	ld a,(de)	
	ld b,5
loop2
	ld (hl),a
	inc hl
	djnz loop2
	inc de
	pop hl
	call Pixel_Address_Down
	pop bc
	djnz loop1

	ld hl,22529
	ld a,66
	ld (hl),a
	inc hl
	ld a,86
	ld (hl),a
	inc hl
	ld a,116
	ld (hl),a
	inc hl
	ld a,101
	ld (hl),a
	inc hl
	ld a,104
	ld (hl),a
	
	ret 

stripes
	defb %00000001
	defb %00000011
	defb %00000111
	defb %00001111
	defb %00011111
	defb %00111111
	defb %01111111
	defb %11111111 

;; Move HL down one pixel line
Pixel_Address_Down
	INC H			; Go down onto the next pixel line
	LD A,H			; Check if we have gone onto next character boundary
	AND 7
	RET NZ			; No, so skip the next bit
	LD A,L			; Go onto the next character line
	ADD A,32
	LD L,A
	RET C			; Check if we have gone onto next third of screen
	LD A,H			; Yes, so go onto next third
	SUB 8
	LD H,A
	RET   

;; Message Strings. Note that the strings are C strings - they are all null terminated.
STR_send        defb "ping",0
STR_titlebar	defb "SpectraMUD v1.0",10,0
STR_looking     defb "Looking up remote host...\r",0
STR_connect     defb "Connected...",10,0
STR_receiving   defb "Receiving data...",10,0
STR_error       defb "Failed!\r",0
STR_done        defb "Done.\r",0

;; Variables
v_sockfd        defb 0          	; storage for socket file descriptor
ip_buffer       defb 192,168,0,43    	; IP address
resp_buffer                     	; put the response after everything else
User avatar
Guesser
Manic Miner
Posts: 639
Joined: Wed Nov 15, 2017 2:35 pm
Contact:

Re: ZX Spectrum Multi User Dungeon (MUD)

Post by Guesser »

I'm just an enthusiastic user and advocate (well I've committed a few minor patches). :)
User avatar
MonkZy
Manic Miner
Posts: 278
Joined: Thu Feb 08, 2018 1:01 pm

Re: ZX Spectrum Multi User Dungeon (MUD)

Post by MonkZy »

Another update on progress.

Big breakthroughs with client-server communications. I have coded a keyboard entry routine. I have coded a main client loop which polls the socket for incoming data, prints data when it arrives, reads keyboard into a buffer and sends the buffer when enter is hit. So basically a fully working (crude) telnet/MUD client.

https://youtu.be/b7J4Kc_96Aw

Apologies for the odd flashes, my desktop grabber is a bit iffy. I also forgot to move my mouse pointer aside. :oops:

ToDo's are :

A text scrolling routine. I am currently using the PRINT42 routine from the SpectraNet ROM. This is great for testing but scrolls the screen in 1/3rds, I want to scroll at least an 8 pixel row at a time. I can put back my title banner once I have a scroll routine working. I also need to add a delete key to the keyboard input.

Text filtering. I need to filter some codes such as the upside-down question mark. I need to add carriage returns to stop words being cut in half due to the 80 column formatting of CircleMUD.

Better error handling. The error routine is vague. I can use the SpectraNet flags to determine what error occurred and print an appropriate message.

Tidy up the MUD files.
Some of the MUD file texts can be re formatted to fit a 42 column display more elegantly.

Future Possibilities :

A 10kb text buffer which can be scrolled back with the up cursor key.

Coupling the MUD server with a TNFS file server and have location images. Use a control code in the MUD descriptions that can point to files on the file system. Scroll the images in with the text.

Customized GUI for shops, combat etc.

The Code :
Spoiler
labels. asm

Code: Select all

;;; Defined Labels

; Hardware page-in entry points
PAGEIN		equ 0x3FF9
PAGEOUT		equ 0x007C
HLCALL		equ 0x3FFA
IXCALL		equ 0x3FFD

; Jump table entry points
SOCKET		equ 0x3E00	; Allocate a socket
CLOSE		equ 0x3E03	; Close a socket
LISTEN		equ 0x3E06	; Listen for incoming connections
ACCEPT		equ 0x3E09	; Accept an incoming connection
BIND		equ 0x3E0C	; Bind a local address to a socket
CONNECT		equ 0x3E0F	; Connect to a remote host
SEND		equ 0x3E12	; Send data 
RECV		equ 0x3E15	; Receive data 

POLL		equ 0x3E1E	; Poll a list of sockets
POLLALL		equ 0x3E21	; Poll all open sockets
POLLFD		equ 0x3E24	; Poll a single socket
GETHOSTBYNAME	equ 0x3E27	; Look up a hostname
PUTCHAR42	equ 0x3E2A	; 42 column print write a character
PRINT42		equ 0x3E2D	; 42 column print a null terminated string
CLEAR42		equ 0x3E30	; Clear the screen and reset 42-col print

LONG2IPSTRING	equ 0x3E39	; Convert a 4 byte big endian long to an IP
IPSTRING2LONG	equ 0x3E3C	; Convert an IP to a 4 byte big endian long
ITOA8		equ 0x3E3F	; Convert a byte to ascii
RAND16		equ 0x3E42	; 16 bit PRNG

GETKEY		equ 0x3E66	; Get a key from the keyboard, and put it in A
KEYUP		equ 0x3E69	; Wait for key release
INPUTSTRING	equ 0x3E6C	; Read a string into buffer at DE

; POLL status bits
BIT_RECV	equ 2
BIT_DISCON	equ 1
BIT_CONN	equ 0

; POLL events/revents bit field
POLLCON		equ 1		; Host has connected to listening socket (not used in spectraMUD)
POLLHUP		equ 2		; The remote host closed the connection
POLLIN		equ 4		; Data is ready to be received
POLLNVAL	equ 128		; An error occurred during polling

;;; Socket Types (added by me from socket.h)

AF_INET		equ 0
SOCK_STREAM	equ 1
SOCK_DGRAM	equ 2
SOCK_RAW	equ 3

;;; System Variables (only available while paged in)

v_column	equ 0x3F00
v_row		equ 0x3F01
v_rowcount	equ 0x3F03
main code.

Code: Select all

; A simple TCP client - this goes along with the following:
; http://spectrum.alioth.net/doc/index.php/Spectranet:_Tutorial_3

        include "labels.asm"

        org 33000               ; Start with RAND USR 33000

        call CLS_42		; clear the screen and set cursor

        ; Open the socket - this simply allocates the resources we
        ; need, and gives us an identifier (file descriptor) for the socket.
        ld c, SOCK_STREAM       ; We want a stream - i.e. TCP socket.
        ld hl, SOCKET           ; Routine to call in ROM - SOCKET.
        call HLCALL
        jp c, Error             ; Exit on error.
        ld (v_sockfd), a        ; Save the socket.

        ; Connect to the remote host. First print a message saying
        ; that this is what we're trying.
	ld ix, PRINT42
        ld hl, STR_connect      
        call IXCALL

        ; ...now connect
        ld a, (v_sockfd)        ; Get the socket file descriptor
        ld de, ip_buffer        ; DE = the address of the IP address in memory
        ld bc, 4000		; we want to connect to port 4000
        ld hl, CONNECT          ; call the CONNECT routine
        call HLCALL
        jp c, Error             ; carry is set on error.

	;; Send a handshake ' ', print a message notifying user of this.
	ld ix, PRINT42
        ld hl, STR_handshake      
        call IXCALL

        ;; Now send some data - ' ' which will elicit a response. 
	;; not sure why, but you must do this to get the MUD to send the motd/login page.
        ld a, (v_sockfd)        ; Get the socket file descriptor
        ld de, STR_send         ; String to send
        ld bc, 1		; which is 1 bytes long
        ld hl, SEND             ; to be passed to the ROM SEND routine
        call HLCALL
        jp c, Error

	ld b,0			; use B to count a short delay between socket polls

	ld hl,23560		; LAST K system variable.
	ld (hl),b		; put null value there.

	;;; And so begins an endless loop 	
Client_Loop
	;; check poll delay
	ld a,b
	cp 0
	jp nz, Skip_Poll

	ld b,255		; reset the delay counter
	push bc			; preserve B for delay counter

	;; Poll the socket for ready data

	ld a, (v_sockfd)	; socket file descriptor
	ld hl, POLLFD
	call HLCALL		; zero flag is set if sockfd is not ready. Flags returned in C
	
	jp z, Skip_Print	; skip printing if sockfd is not ready

	ld a, c			; load the events flags into A
	cp POLLIN		; check if data is ready to be recieved
	jp nz, Poll_Error	; no data, most likely the host has closed the connection.
	
	call Print_Text		; print incoming text
Skip_Print
	pop bc

Skip_Poll
	push bc			; preserve B for delay counter
 
	;; Read the keyboard

	ld hl,23560		; LAST K system variable.
	ld a,(hl)
	cp 0			; check for null
	jr z, non_printable	; no key has been pressed, loop back
	ld b,0
	ld (hl),b		; put null value there.

	cp 13
	jp z, return		; check if ENTER has been pressed

	cp 32
	jp c, non_printable	; check if input char is out of ASCII range (will be a cursor,inverse video or delete)

	cp 123
	jp nc, non_printable	; check if input char is out of ASCII range (not sure if this is possible)

	ld b,a			; put input char into B for safekeeping

	ld a,(kb_size)
	cp 86
	jp z,non_printable	; check if the key buffer is full

	inc a
	ld (kb_size),a		; increase the buffer size variable by one byte

	ld a,b			; retrieve the input char
	ld hl,(kb_pointer)	; load HL with the key buffer pointer address
	ld (hl),a		; store the char in the buffer
	inc hl			; increment the buffer pointer
	ld (kb_pointer),hl	; store the buffer pointer

	ld hl,PUTCHAR42
	call HLCALL

non_printable
	pop bc			; retrieve B for delay counter
	dec b			; decriment the delay counter
	jp Client_Loop

	;; We have an ENTER, send the command to the MUD!!
return
	
	ld de,(kb_pointer)	; load DE with the key buffer pointer address
	ld hl,STR_terminator	; load HL with the CR+LF string address
	ld bc, 2
	ldir			; store the chars in the buffer
	
	ld a, (kb_size)
	ld c,a
	ld b,0			; load BC with the byte length of the buffer
        ld a, (v_sockfd)        ; load A with the socket file descriptor
        ld de, kb_buffer	; load DE with the address of the keyboard buffer
        
        ld hl, SEND             ; to be passed to the ROM SEND routine
        call HLCALL
        jp c, Error

	;; reset the keyboard buffer
	xor a			; Load A with zero
	ld hl,kb_buffer
	ld b,86		; fill the buffer with zeros
reset_loop
	ld (hl),a		; store a zero into buffer
	inc hl
	djnz reset_loop

	ld bc,kb_buffer
	ld (kb_pointer),bc	; reset the key buffer pointer

	ld a,8
	ld (kb_size),a		; reset the key buffer size

	pop bc			; retrieve B for delay counter
	ld b,0			; reset the delay counter

	;; print a carriage return
	ld a,10
	ld hl,PUTCHAR42
	call HLCALL

	jp Client_Loop

	;; Exit cleanly to BASIC from main loop
exit
        call Close_Socket

	ld ix, PRINT42
        ld hl, STR_done         ; Print 'Done'
        call IXCALL
	pop bc			; empty the stack for a clean return to BASIC
        ret	

Print_Text
        ld a, (v_sockfd)
        ld de, resp_buffer      ; the buffer we want to fill.
        ld bc, 1024             ; up to 1K at a time
        ld hl, RECV             ; and use the ROM's RECV routine
        call HLCALL
  
        ; Make sure the returned buffer has a null on the end for
        ; when we go to print it.
        ld hl, resp_buffer
        add hl, bc              ; BC contains the number of bytes received.
        ld (hl), 0              ; put a NULL on the end

	ld ix, PRINT42
        ld hl, resp_buffer      ; and print the data we received
        call IXCALL

	ld ix, PRINT42
        ld hl, kb_buffer	; print the text input buffer
        call IXCALL

	ret



	;; Error while polling, if the host closed the connection message the user
Poll_Error
	cp POLLHUP		; check if host has closed the connection
	jp nz,Error		; it must be an error during polling
	ld ix, PRINT42
        ld hl, STR_host_closed	; Host has closed the connection string
        call IXCALL             ; print it
	pop bc			; empty the stack for a clean return to BASIC
	jp Close_Socket		

	;; Exit on error, and print a vague string.
Error
	ld ix, PRINT42
        ld hl, STR_error        ; Error string
        call IXCALL             ; print it

	;; Close the socket and exit
Close_Socket
        ld a, (v_sockfd)
        and a                   ; is the socket set?
        ret z                   ; no.. but if it is
        ld hl, CLOSE            ; clean up the socket
        call HLCALL
        ret        

	;; Clear the screen, set PRINT42 cursor and set the title bar.
CLS_42
	ld a, 7
	out (254), a		; white border
	ld hl, 16384
	ld de, 16385
	ld bc, 6144
	ld (hl), l
	ldir
	ld (hl), 56		; bright 1/black paper/white ink (71 for banner)
	ld bc, 32
	ldir
	ld (hl), 56		; white paper/black ink 
	ld bc, 735
	ldir
	call PAGEIN
	ld a,0			; 9 for banner
	ld (v_column), a
	xor a
	ld (v_rowcount), a
	ld hl, 16384
	ld (v_row), hl

	jp skip_banner		; skipping banner for now
 
        ld hl, STR_titlebar	; Title bar string
        call PRINT42		; print it
	call PAGEOUT
	ld hl, 16385
	ld de, stripes

	ld b,8
loop1
	push bc
	push hl
	ld a,(de)	
	ld b,5
loop2
	ld (hl),a
	inc hl
	djnz loop2
	inc de
	pop hl
	call Pixel_Address_Down
	pop bc
	djnz loop1

	ld hl,22529
	ld a,66
	ld (hl),a
	inc hl
	ld a,86
	ld (hl),a
	inc hl
	ld a,116
	ld (hl),a
	inc hl
	ld a,101
	ld (hl),a
	inc hl
	ld a,104
	ld (hl),a
skip_banner	
	ret 

stripes
	defb %00000001
	defb %00000011
	defb %00000111
	defb %00001111
	defb %00011111
	defb %00111111
	defb %01111111
	defb %11111111 

	;; Move HL down one pixel line
Pixel_Address_Down
	INC H			; Go down onto the next pixel line
	LD A,H			; Check if we have gone onto next character boundary
	AND 7
	RET NZ			; No, so skip the next bit
	LD A,L			; Go onto the next character line
	ADD A,32
	LD L,A
	RET C			; Check if we have gone onto next third of screen
	LD A,H			; Yes, so go onto next third
	SUB 8
	LD H,A
	RET   

;; Message Strings. Note that the strings are C strings - they are all null terminated.
STR_send        defb 32,"ping",13,10,0
STR_titlebar	defb "SpectraMUD v1.0",10,0
STR_cursor	defb 10,">",0
STR_looking     defb "Looking up remote host...",10,0
STR_connect     defb "Connecting to MUD...",10,0
STR_handshake   defb "Sending handshake...",10,0
STR_host_closed	defb 10,"The host has closed the connection!",0
STR_error       defb 10,"An error has occured!",0
STR_done        defb 10,"Done.",0
STR_not_null	defb "nn",0
STR_terminator	defb 13,10	; CR+LF

;; Keyboard buffer and variables (reserve 84 bytes, two lines of text)
kb_buffer
	defb 0,0,0,0,0,0,0,0,0,0
	defb 0,0,0,0,0,0,0,0,0,0
	defb 0,0,0,0,0,0,0,0,0,0
	defb 0,0,0,0,0,0,0,0,0,0
	defb 0,0,0,0,0,0,0,0,0,0
	defb 0,0,0,0,0,0,0,0,0,0
	defb 0,0,0,0,0,0,0,0,0,0
	defb 0,0,0,0,0,0,0,0,0,0
	defb 0,0,0,0
	defb 0,0			; extra 2 bytes for CR+LF termination
kb_pointer
	defw kb_buffer
kb_size
	defb 2

;; Variables
v_sockfd        defb 0          	; storage for socket file descriptor
ip_buffer       defb 192,168,0,43    	; IP address
resp_buffer                     	; put the response after everything else
User avatar
Einar Saukas
Bugaboo
Posts: 3070
Joined: Wed Nov 15, 2017 2:48 pm

Re: ZX Spectrum Multi User Dungeon (MUD)

Post by Einar Saukas »

This is looking great!

It has the characteristics of a project that may continue to evolve over time. For this reason, I suggest you move this project to Github and post a link here, instead of posting copies of your code directly.

This will have many advantages. People will be able to easily find the latest version (instead of searching through many posts), it will take a single click to download and try it, it will give your project more visibility, you will have a good place to add an intro page explaining it later, we will be able to add it to ZXDB later with a direct link to it, and so on.
User avatar
Quazar
Drutt
Posts: 31
Joined: Tue Jun 16, 2020 2:28 pm
Contact:

Re: ZX Spectrum Multi User Dungeon (MUD)

Post by Quazar »

This sounds very interesting, I'll have to keep an eye on it! (I was playing/coding on a MUD way, way back in 1992 or so, seriously addictive stuff!). While I don't have a Spectranet for my ZX Spectrums I do have the Next dev board and could also help with adapting the client for the SAM Coupé as I gave that an ethernet connection with my Trinity interface.
Quazar - Developing for the SAM Coupé for 30+ Years!
Hardware, Software, 'SAM Revival' magazine -> www.samcoupe.com
Plus hardware for the ZX Spectrum, RC2014 and other general retro peripherals.
User avatar
MonkZy
Manic Miner
Posts: 278
Joined: Thu Feb 08, 2018 1:01 pm

Re: ZX Spectrum Multi User Dungeon (MUD)

Post by MonkZy »

Einar Saukas wrote: Mon Jul 06, 2020 2:06 pm For this reason, I suggest you move this project to Github and post a link here, instead of posting copies of your code directly.
I will look into this. I am not a trained programmer so I am never confident my code is acceptable. I strive to get a piece of code to work, and gain satisfaction when it does what I want it to do. I am never certain I took the best approach. Github does look like a great way to organize and present a project, so I will set one up soon. Thanks for the advice.

Quazar wrote: Mon Jul 06, 2020 8:52 pm (I was playing/coding on a MUD way, way back in 1992 or so, seriously addictive stuff!).
The MUD server side of things is pretty overwhelming to be honest. There is so much to learn just to get something basic working. I will try to get some Spectrum themed areas working but most likely I will run a slightly altered default CircleMUD (Midgaard, New Thalos etc.) server for any testing. The base world on CircleMUD is pretty huge and fun to explore if you have never played in it. I have never played a stock DikuMUD so it is new content to me. Ultimately I want to have an area aimed at Spectrum users, with a portal into the circleMUD content. I may ask for advice when I get to that stage. I am totally new to the server side of MUD's. I only played in heavily populated MUD's and never got high enough in a guild to get close to editing the content. Wrote a bunch of client side scripts and macro's though. Aye, can be quite addictive.

An eventual NEXT and SAM client would be cool :D I really wanted a SAM back in the day. By the time I had the income to buy one, the ship had sailed and I went the Amiga route.
User avatar
ketmar
Manic Miner
Posts: 610
Joined: Tue Jun 16, 2020 5:25 pm
Location: Ukraine

Re: ZX Spectrum Multi User Dungeon (MUD)

Post by ketmar »

MonkZy wrote: Tue Jul 07, 2020 1:16 am
Einar Saukas wrote: Mon Jul 06, 2020 2:06 pm For this reason, I suggest you move this project to Github and post a link here, instead of posting copies of your code directly.
I will look into this. I am not a trained programmer so I am never confident my code is acceptable.
you don't have to publish some "perfect" code. you don't have to be a "trained professional". that's the whole idea of FOSS -- start something you want to do, and other people will join and help you if they'll like it.

look at a source code repository as if it is just a backup archive. that archive doesn't have to be in a "perfect" state. and there is no authority to "accept" your code (besides Z80, of course ;-). you just copying your working files to a remove server (with some comments). most people will never expect your commits to be always perfect.

sorry for this long text. i just wanted to encourage you a little. ;-)
User avatar
Quazar
Drutt
Posts: 31
Joined: Tue Jun 16, 2020 2:28 pm
Contact:

Re: ZX Spectrum Multi User Dungeon (MUD)

Post by Quazar »

MonkZy wrote: Tue Jul 07, 2020 1:16 am CircleMUD (Midgaard, New Thalos etc.) server for any testing.
Not familiar with CircleMUD, admittedly though it's been nearly 3 decades since I was spending hours a day on one. It was a lpMUD I was programming for.
MonkZy wrote: Tue Jul 07, 2020 1:16 am I really wanted a SAM back in the day. By the time I had the income to buy one, the ship had sailed and I went the Amiga route.
I was late to the SAM scene, didn't get my first until Spring 1993!
Quazar - Developing for the SAM Coupé for 30+ Years!
Hardware, Software, 'SAM Revival' magazine -> www.samcoupe.com
Plus hardware for the ZX Spectrum, RC2014 and other general retro peripherals.
User avatar
MonkZy
Manic Miner
Posts: 278
Joined: Thu Feb 08, 2018 1:01 pm

Re: ZX Spectrum Multi User Dungeon (MUD)

Post by MonkZy »

Update....

Progress has slowed due to work and other distractions but some advances have been made. I have written my own text scrolling routine which is a vast improvement on the scroll used by the spectraNet ROM. I will post the scroll code below. I am very open to suggestions for this routine. The routine + lookup table costs 404 bytes. I am sure there are ways to reduce the size without reducing the speed. I have also added some text formatting to stop words being cut in half. Things look OK, but the MUD data has a carriage return at the end of each 80 character line so some lines leave excess white space at the ends. Really to improve the text formatting, the MUD data would have to be changed. Editing the default circleMUD world would take many hours. I have spent a few hours learning how to structure the MUD files and have added a few new locations and a portal system to gain access to the default circleMUD world. The new locations are more friendly to the 42 char client.

https://www.youtube.com/watch?v=mMpVs27dS00

The assembly for my 52 byte scroll routine.

Code: Select all

;;; scroll the text window up one row
;;; B = number of rows to scroll up

Scroll_Up
	push bc
	halt
	ld b,168		; we will loop for 168 lines of screen memory
scroll_loop
	push bc

	;; source and destination are referenced from a lookup table (screen-lookup.asm)
source
	ld hl,(screen_lookup+16)	; source is 8 lines below destination (each line is a 2 byte address)
destination
	ld de,(screen_lookup)
	ld bc,32			; copy 32 bytes, the length of one line
	ldir				; perform copy

	;; increment the source and destination lookup pointers
	ld hl,(destination+2)	; ld de,(NN) uses ED prefix hence +2
	inc hl
	inc hl
	ld (destination+2),hl
	ld hl,(source+1)
	inc hl
	inc hl
	ld (source+1),hl

	pop bc
	djnz scroll_loop

	;; reset the source and destination pointers
	ld hl,screen_lookup+16
	ld (source+1),hl
	ld hl,screen_lookup
	ld (destination+2),hl

	pop bc
	djnz Scroll_Up
The routine uses a 352 byte lookup table :
Spoiler

Code: Select all

;;; Lookup table of the address of the first byte of each pixel line

;;; Row 00
     ;defw 16384 ; 000
     ;defw 16640 ; 001
     ;defw 16896 ; 002
     ;defw 17152 ; 003
     ;defw 17408 ; 004
     ;defw 17664 ; 005
     ;defw 17920 ; 006
     ;defw 18176 ; 007
;;; Row 01
screen_lookup		;this label marks the top of the scroll window
     defw 16416 ; 008
     defw 16672 ; 009
     defw 16928 ; 010
     defw 17184 ; 011
     defw 17440 ; 012
     defw 17696 ; 013
     defw 17952 ; 014
     defw 18208 ; 015
;;; Row 02
     defw 16448 ; 016
     defw 16704 ; 017
     defw 16960 ; 018
     defw 17216 ; 019
     defw 17472 ; 020
     defw 17728 ; 021
     defw 17984 ; 022
     defw 18240 ; 023
;;; Row 03
     defw 16480 ; 024
     defw 16736 ; 025
     defw 16992 ; 026
     defw 17248 ; 027
     defw 17504 ; 028
     defw 17760 ; 029
     defw 18016 ; 030
     defw 18272 ; 031
;;; Row 04
     defw 16512 ; 032
     defw 16768 ; 033
     defw 17024 ; 034
     defw 17280 ; 035
     defw 17536 ; 036
     defw 17792 ; 037
     defw 18048 ; 038
     defw 18304 ; 039
;;; Row 05
     defw 16544 ; 040
     defw 16800 ; 041
     defw 17056 ; 042
     defw 17312 ; 043
     defw 17568 ; 044
     defw 17824 ; 045
     defw 18080 ; 046
     defw 18336 ; 047
;;; Row 06
     defw 16576 ; 048
     defw 16832 ; 049
     defw 17088 ; 050
     defw 17344 ; 051
     defw 17600 ; 052
     defw 17856 ; 053
     defw 18112 ; 054
     defw 18368 ; 055
;;; Row 07
     defw 16608 ; 056
     defw 16864 ; 057
     defw 17120 ; 058
     defw 17376 ; 059
     defw 17632 ; 060
     defw 17888 ; 061
     defw 18144 ; 062
     defw 18400 ; 063
;;; Row 08
     defw 18432 ; 064
     defw 18688 ; 065
     defw 18944 ; 066
     defw 19200 ; 067
     defw 19456 ; 068
     defw 19712 ; 069
     defw 19968 ; 070
     defw 20224 ; 071
;;; Row 09
     defw 18464 ; 072
     defw 18720 ; 073
     defw 18976 ; 074
     defw 19232 ; 075
     defw 19488 ; 076
     defw 19744 ; 077
     defw 20000 ; 078
     defw 20256 ; 079
;;; Row 10
     defw 18496 ; 080
     defw 18752 ; 081
     defw 19008 ; 082
     defw 19264 ; 083
     defw 19520 ; 084
     defw 19776 ; 085
     defw 20032 ; 086
     defw 20288 ; 087
;;; Row 11
     defw 18528 ; 088
     defw 18784 ; 089
     defw 19040 ; 090
     defw 19296 ; 091
     defw 19552 ; 092
     defw 19808 ; 093
     defw 20064 ; 094
     defw 20320 ; 095
;;; Row 12
     defw 18560 ; 096
     defw 18816 ; 097
     defw 19072 ; 098
     defw 19328 ; 099
     defw 19584 ; 100
     defw 19840 ; 101
     defw 20096 ; 102
     defw 20352 ; 103
;;; Row 13
     defw 18592 ; 104
     defw 18848 ; 105
     defw 19104 ; 106
     defw 19360 ; 107
     defw 19616 ; 108
     defw 19872 ; 109
     defw 20128 ; 110
     defw 20384 ; 111
;;; Row 14
     defw 18624 ; 112
     defw 18880 ; 113
     defw 19136 ; 114
     defw 19392 ; 115
     defw 19648 ; 116
     defw 19904 ; 117
     defw 20160 ; 118
     defw 20416 ; 119
;;; Row 15
     defw 18656 ; 120
     defw 18912 ; 121
     defw 19168 ; 122
     defw 19424 ; 123
     defw 19680 ; 124
     defw 19936 ; 125
     defw 20192 ; 126
     defw 20448 ; 127
;;; Row 16
     defw 20480 ; 128
     defw 20736 ; 129
     defw 20992 ; 130
     defw 21248 ; 131
     defw 21504 ; 132
     defw 21760 ; 133
     defw 22016 ; 134
     defw 22272 ; 135
;;; Row 17
     defw 20512 ; 136
     defw 20768 ; 137
     defw 21024 ; 138
     defw 21280 ; 139
     defw 21536 ; 140
     defw 21792 ; 141
     defw 22048 ; 142
     defw 22304 ; 143
;;; Row 18
     defw 20544 ; 144
     defw 20800 ; 145
     defw 21056 ; 146
     defw 21312 ; 147
     defw 21568 ; 148
     defw 21824 ; 149
     defw 22080 ; 150
     defw 22336 ; 151
;;; Row 19
     defw 20576 ; 152
     defw 20832 ; 153
     defw 21088 ; 154
     defw 21344 ; 155
     defw 21600 ; 156
     defw 21856 ; 157
     defw 22112 ; 158
     defw 22368 ; 159
;;; Row 20
     defw 20608 ; 160
     defw 20864 ; 161
     defw 21120 ; 162
     defw 21376 ; 163
     defw 21632 ; 164
     defw 21888 ; 165
     defw 22144 ; 166
     defw 22400 ; 167
;;; Row 21
output_line
     defw 20640 ; 168
     defw 20896 ; 169
     defw 21152 ; 170
     defw 21408 ; 171
     defw 21664 ; 172
     defw 21920 ; 173
     defw 22176 ; 174
     defw 22432 ; 175
;;; Row 22

     defw 20672 ; 176
     defw 20928 ; 177
     defw 21184 ; 178
     defw 21440 ; 179
     defw 21696 ; 180
     defw 21952 ; 181
     defw 22208 ; 182
     defw 22464 ; 183
;;; Row 23
status_line
     defw 20704 ; 184
     ;defw 20960 ; 185
     ;defw 21216 ; 186
     ;defw 21472 ; 187
     ;defw 21728 ; 188
     ;defw 21984 ; 189
     ;defw 22240 ; 190
     ;defw 22496 ; 191
I have also set up a TNFS server on the Pi alongside the MUD server. I will be adding in-line images very soon.

Imminent ToDo's


- Delete key. Still not got one.
- 10kb text buffer, allowing the user to scroll back text to re-read it.
- A 'More..' prompt when the incoming data goes over 21 lines. Client will wait for a space bar before continuing.

I am pretty much ready to put this thing online for testing. My only reservation at this point is security. How much of a risk is forwarding a couple of ports (TCP for the MUD, UDP for the TNFS server) to a Pi running on a standard broadband router?
I could afford to rent some server space and host the two servers on that which would remove the need to publish my home IP, but if it is relatively safe to run TNFS/MUD from home I would do it that way and forgo the cost.
I have set up the TNFS server to deliver the latest version of the SpectraMUD client, so you can set it up as a default filesystem and boot straight into the latest version.
User avatar
Guesser
Manic Miner
Posts: 639
Joined: Wed Nov 15, 2017 2:35 pm
Contact:

Re: ZX Spectrum Multi User Dungeon (MUD)

Post by Guesser »

Definitely set up your file permissions to make the tnfs root read only to the daemon if it's a public server. You can run tnfsd 'chroot' too.

I love the smooth scrolling.
User avatar
8BitAG
Dynamite Dan
Posts: 1487
Joined: Sun Dec 17, 2017 9:25 pm
Contact:

Re: ZX Spectrum Multi User Dungeon (MUD)

Post by 8BitAG »

Interesting stuff. I spent a good year or so, solidly playing MUDs and MUSHes when I was at Uni in 93/94. Too long probably. :) As a text adventure fan it was a natural first step in online gaming. Was a regular on Uglymug and EleMUD... It's quite cool to see that they're still around... I even logged in to my old Uglymug account a few years ago and wandered around the rooms I created... Sadly my EleMUD account was long gone and the landscape was almost completely different to what I remembered.

Certainly a fun project. I've seen similar things done on other platforms too. On the BBC they''ve recreated some of the old MUDs in an offline single-player form which is also an interesting way of capturing some elements of the experience.
8-bit Text Adventure Gamer - games - research.
Post Reply