145 lines
6.0 KiB
Markdown
145 lines
6.0 KiB
Markdown
|
# Hermes project report
|
||
|
|
||
|
## Douglas Richardson
|
||
|
### April 28, 2017
|
||
|
|
||
|
# Overview
|
||
|
Hermes is a multi-user chat program that allows users to setup a server, connect to it
|
||
|
and communicate with all other members of the server.
|
||
|
|
||
|
Hermes uses TCP pipes to tread input and output like a port. Essentially, each client sends
|
||
|
information to the server and depending on the input, the server decides what to do with it
|
||
|
and usually sends output back to all the other users.
|
||
|
|
||
|
# Libraries Used
|
||
|
The code uses two non-default libraries:
|
||
|
|
||
|
```
|
||
|
(require racket/gui/base)
|
||
|
(require math/base)
|
||
|
```
|
||
|
|
||
|
* The ```racket/gui/base``` library is the primary library for the GUI.
|
||
|
* the ```math/base``` is used for random number generation.
|
||
|
|
||
|
# Key Code Excerpts
|
||
|
|
||
|
Here is a discussion of the most essential procedures, including a description of how they embody ideas from
|
||
|
UMass Lowell's COMP.3010 Organization of Programming languages course.
|
||
|
|
||
|
Five examples are shown and they are individually numbered.
|
||
|
|
||
|
## 1. Initializing the gui
|
||
|
|
||
|
This line of code allows us to wrap the gui into an object.
|
||
|
|
||
|
```
|
||
|
(define (make-gui)
|
||
|
...
|
||
|
(cond ((eq? command 'show) (lambda () (send main-frame show #t)))
|
||
|
((eq? command 'get-color) get-my-color)
|
||
|
((eq? command 'set-color) set-color)
|
||
|
((eq? command 'prompt-color) prompt-color)
|
||
|
((eq? command 'prompt-username) prompt-username)
|
||
|
((eq? command 'prompt-hostname) prompt-hostname)
|
||
|
((eq? command 'send) send-message) ;; call to show a message in a gui
|
||
|
((eq? command 'set-name) (lambda (newname) (if (string? newname)
|
||
|
(set! name newname)
|
||
|
(print "Thats not good"))))
|
||
|
; ((eq? command 'recieve-message) user-message)
|
||
|
; ((eq? command 'get-list) listy)
|
||
|
; ((eq? command 'set-list) update)
|
||
|
;;Something up with that
|
||
|
; else should assume a message and output to screen we do not want it
|
||
|
; to fail
|
||
|
((eq? command 'get-message) get-message)
|
||
|
(else (error "Invalid Request" command))
|
||
|
))
|
||
|
;;dispatch goes below that
|
||
|
dispatch)```
|
||
|
|
||
|
This allows us to make our code simpler and lets us treat the gui like an object in it's self.
|
||
|
Giving the gui commands to change it's self rather than having to remember all the commands it has.
|
||
|
|
||
|
## 2. Working with lists
|
||
|
|
||
|
This code is code that allows us to append a new message onto the end of the list of messages using recursion
|
||
|
|
||
|
```
|
||
|
(define (appendlist listoflist add-to-end)
|
||
|
(if (null? listoflist)
|
||
|
(cons add-to-end '())
|
||
|
(cons (car listoflist) (appendlist (cdr listoflist) add-to-end))))```
|
||
|
|
||
|
Normally there is a function to just append onto the end of a list, however the problem is that if we attempt to append
|
||
|
a list of elements onto the end of a list, it just appends the elements onto the end of the list. For example if I had
|
||
|
a list of the following '(("Doug" "Hello World!" "Purple"))
|
||
|
and wanted to append the list '("Gordon" "No one else is here Doug." "Black") The list I want back would be
|
||
|
'(("Doug" "Hello World!" "Purple")("Gordon" "No one else is here Doug." "Black")) but if I use the default
|
||
|
list append I get'(("Doug" "Hello World!" "Purple")"Gordon" "No one else is here Doug." "Black")
|
||
|
which is no good for the gui.
|
||
|
|
||
|
This follows on our idea of working with lists and using recursion to walk down a list.
|
||
|
|
||
|
## 3. Re-drawing messages
|
||
|
|
||
|
The following procedure is used to re-draw messages onto the canvas after a screen move or resize.
|
||
|
|
||
|
```
|
||
|
(define (update-helper given-list)
|
||
|
(if (null? given-list)
|
||
|
'()
|
||
|
(if (null? (car given-list))
|
||
|
'()
|
||
|
(begin (user-message
|
||
|
(get-username-from-list (car given-list))
|
||
|
(get-message-from-list (car given-list))
|
||
|
(get-color-from-list (car given-list)))
|
||
|
(update-helper (cdr given-list))))))```
|
||
|
|
||
|
While it doesn't actually use the map function, this is a map as for every element of a list (each element is a list of three strings)
|
||
|
it runs a procedure (or in this case a set of procedures) in the order of the list.
|
||
|
|
||
|
## 4. Parsing Messages
|
||
|
|
||
|
This line of code is used to parse a single string message into a three string message
|
||
|
|
||
|
```
|
||
|
(define (user-message-parse string-i start)
|
||
|
(define (helper str index)
|
||
|
(if (eq? (string-ref str (+ start index)) #\~) ; regexes would allow us
|
||
|
; to avoid this #\~
|
||
|
(substring str start (+ start index))
|
||
|
(helper str (+ index 1))))
|
||
|
(helper string-i 0))```
|
||
|
|
||
|
This was used to parse a string into smaller strings. In hermes we can only send one string to each client at one time, therefore
|
||
|
the three elements that the gui uses to print messages need to be compressed together. We append a ~ inbetween each of these so we can
|
||
|
parse them out at the client end.
|
||
|
|
||
|
While we don't run any commands off it (saved that part for the commands we do interpret from strings)
|
||
|
it is similar to the symbolic differentaitor.
|
||
|
|
||
|
## 5. Color setting
|
||
|
Here we have an example of when we use a symbolic differentiator in the gui to determine when a user wants to run a command
|
||
|
rather than input text.
|
||
|
|
||
|
```
|
||
|
(define (button-do-stuff b e);b and e do nothing :/
|
||
|
(if (color-change-request? (send input get-value))
|
||
|
(set! my-color (get-color-from-input (send input get-value)))
|
||
|
...
|
||
|
|
||
|
(define (color-change-request? given-string)
|
||
|
(if (> (string-length given-string) 7)
|
||
|
(if (equal? (substring given-string 0 6) "/color")
|
||
|
#t
|
||
|
#f)
|
||
|
#f))```
|
||
|
|
||
|
The procedure button-do-stuff is run every time the user presses the return key or presses the send button on the gui
|
||
|
and what it will do is check to see if the user typed in "/color", and if they did it sets the internal color to be
|
||
|
what the user said after that. This is part of our symbolic differentiator that allows the user to use commands
|
||
|
rather than the typical use of the input (which is just to send a message to other clients)
|
||
|
|