0
0
Fork 0

Merge pull request #41 from Oliver-Akins/dev

V1.2.0
This commit is contained in:
Oliver 2021-01-09 15:14:20 -07:00 committed by GitHub
commit 7ac1b4c110
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 236 additions and 156 deletions

3
.gitignore vendored
View file

@ -5,7 +5,8 @@ node_modules
server/server.toml server/server.toml
server/built/* server/built/*
server/dist/* server/dist/*
server/resources/games server/resources/*
*.log
#=============================================================================# #=============================================================================#
# The files that were auto-generated into a .gitignore by Vue-cli # The files that were auto-generated into a .gitignore by Vue-cli

52
server/README.md Normal file
View file

@ -0,0 +1,52 @@
# Setup:
1. `cd` into this `server` directory.
2. Run `pnpm install` to install all of the required dependencies.
3. Create a copy of the `template.toml` file, and name it `server.toml`.
4. Edit the `server.toml` file to adjust the
5. Run `tsc` to compile the TypeScript into Javascript. This should create a
`dist` directory.
## Using systemd to manage the server: (Not currently implemented)
This app comes with a `ghost-writer.service` file which is already set up to
manage the server, it just requires a little bit of additional setup. If you
change any of the symlinking in the steps below, it is your responsibility to
figure it out, I will not guarantee support for people who attempt to modify
the service file.
6. Create a symlink named `server` in the server root (`/`) pointing to the
server folder in the Ghost Writer git repository.
(Ex: `sudo ln -s ~/Ghost-Writer-Online/server /server`)
7. Create a symlink named `ghost-writer.service` in `/etc/systemd/system`
pointing to the service file in the `server` folder of the Ghost Writer Online
repository. (Ex: `sudo ln -s /etc/systemd/system/ghost-writer.service /server/ghost-writer.service`)
8. Start the websocket server with `sudo systemctl start ghost-writer`.
9. Make sure the server is started by running `systemctl status ghost-writer`
* To restart the server, run `sudo systemctl restart ghost-writer`.
* To stop the server, run `sudo systemctl stop ghost-writer`.
* To access the server logs, run `sudo journalctl -u ghost-writer`
## Using a detached terminal:
If you would prefer that the server does not automatically restart and you can
more easily output log output to a file, you can run the server by following
the below steps:
6. Make sure you know how to invoke a single command with your preferred method
of detaching terminals.
7. Have the detached terminal's working directory be in the `server` folder of
this repository.
8. Run `node dist/main.js` for logging directly to the CLI, or
`node dist/main.js > output.log` for logging to a file named `output.log`.
9. Detach your terminal.
---
# Configuration:
All of the configuration for the server is done in the `server.toml` with
explanations in the file. When a value is changed in the config, you will need
to restart the server. This is either through systemd
(`sudo systemctl restart ghost-writer`) or through your preferred terminal
detacher, by stopping then re-starting the process.

View file

@ -0,0 +1,9 @@
[Unit]
Description=Ghost Writer websocket server
[Service]
Type=simple
WorkingDirectory=/server
ExecStart=node /server/dist/main.js
Restart=always
RestartSec=5

View file

@ -1,21 +0,0 @@
Answer 1,Answer 2,Answer 3,Answer 4,Answer 5
microphone,lamp,chair,crown,brain
screwdriver,chainsaw,sunglasses,dinosaur,tree
flag,ship,pie,pineapple,helmet
ruler,pen,guitar,skateboard,tomato
bathtub,computer,whale,telescope,submarine
flower,blanket,pacifier,harp,acorn
window,doll,stove,skeleton,vacuum
printer,shoe,bed,fork,shield
catapult,apple,envelope,broom,windmill
flashlight,pyramid,brick,bicycle,truck
train,horse,knife,sponge,shirt
newspaper,clock,basketball,mirror,backpack
phone,car,dragon,platypus,scissors
broccoli,shovel,scorpion,sandwich,piano
pillow,pencil,ladder,laptop,headphones
tent,toothbrush,hammer,coin,peanut
bottle,key,pants,fish,pizza
bowl,bread,wheelchair,rope,giraffe
toilet,house,cloak,soap,banana
painting,tractor,sword,book,umbrella
1 Answer 1 Answer 2 Answer 3 Answer 4 Answer 5
2 microphone lamp chair crown brain
3 screwdriver chainsaw sunglasses dinosaur tree
4 flag ship pie pineapple helmet
5 ruler pen guitar skateboard tomato
6 bathtub computer whale telescope submarine
7 flower blanket pacifier harp acorn
8 window doll stove skeleton vacuum
9 printer shoe bed fork shield
10 catapult apple envelope broom windmill
11 flashlight pyramid brick bicycle truck
12 train horse knife sponge shirt
13 newspaper clock basketball mirror backpack
14 phone car dragon platypus scissors
15 broccoli shovel scorpion sandwich piano
16 pillow pencil ladder laptop headphones
17 tent toothbrush hammer coin peanut
18 bottle key pants fish pizza
19 bowl bread wheelchair rope giraffe
20 toilet house cloak soap banana
21 painting tractor sword book umbrella

View file

@ -1,82 +0,0 @@
Question Text
Where would I be most likely to find it?
What continent/region would I find the most of these?
What country am I most likely to find it in?
What city am I most likely to find it in?
Where in my house am I most likely to find it?
What kind of store am I most likely to find it in?
What era did it first appear in?
What's something that's about as dangerous as it?
What's a variety it comes in?
What subcategory does it fall under?
What's something that's like it?
What color is it most commonly?
What material is it made of?
What does it feel like if I touch it?
How is it made?
What is it used for?
What's a common brand of it?
What's it about the same size as?
What's it about the same weight as?
What does it cost about the same as?
What would happen if I ate it?
What's your favorite version/variety of it?
Who's a fictional character that has/uses it?
Who's a celebrity that has/uses it?
What kind of people commonly have or use it?
What abstract concept is associated with it?
What does it smell like?
What does it taste like?
What noise does it make when dropped?
Who at this table likes it the most?
"If it was an animal, what animal would it be?"
What's a book or movie/TV that it appears prominently in?
What age group likes it the most?
How do you feel about it?
Who dislikes it?
What time of day is it used?
What do I wear when I use it?
Why do people use it?
With what frequency do most people use it?
What part of the body do I use it with?
What do I clean it with?
How is it transported?
Where is it stored?
Where do you use it?
What holiday is it most associated with?
What would I use to write on it?
What's a game that it appears in?
What climate is it most associated with?
What website can I find it on?
How long will it last if left alone?
How does it affect the things around it?
Where would I get one?
How would it react to being pushed down a hill?
What happens if I light it on fire?
What happens if I put it under water?
What superpower would this thing want?
How would I use it as a weapon?
How do I feel after using it?
What would happen if I bent it?
What would I use to destroy it?
What's inside it?
What's its opposite?
What profession works with it?
"If it were a musical instrument, what instrument would it be?"
"If it was a social media website, what site would it be?"
What genre of movie/book is most likely to feature it?
"If it had a favorite food, what would it be?"
What's something you can make using it?
What powers it?
What container would I keep it in?
What's the secondary material it's made of? (not its main material)
Who or what can lift it?
What ancient Greek/Roman god is it most associated with?
What habitat would I be most like to find it in?
What is a group of them called?
Who or what makes it?
What field of science studies it?
"Where does it go when it dies, breaks, or is no longer useful?"
What superhero is it most related to?
What month would I find it or use it in?
"If it was an alcoholic drink, what would it be?"
1 Question Text
2 Where would I be most likely to find it?
3 What continent/region would I find the most of these?
4 What country am I most likely to find it in?
5 What city am I most likely to find it in?
6 Where in my house am I most likely to find it?
7 What kind of store am I most likely to find it in?
8 What era did it first appear in?
9 What's something that's about as dangerous as it?
10 What's a variety it comes in?
11 What subcategory does it fall under?
12 What's something that's like it?
13 What color is it most commonly?
14 What material is it made of?
15 What does it feel like if I touch it?
16 How is it made?
17 What is it used for?
18 What's a common brand of it?
19 What's it about the same size as?
20 What's it about the same weight as?
21 What does it cost about the same as?
22 What would happen if I ate it?
23 What's your favorite version/variety of it?
24 Who's a fictional character that has/uses it?
25 Who's a celebrity that has/uses it?
26 What kind of people commonly have or use it?
27 What abstract concept is associated with it?
28 What does it smell like?
29 What does it taste like?
30 What noise does it make when dropped?
31 Who at this table likes it the most?
32 If it was an animal, what animal would it be?
33 What's a book or movie/TV that it appears prominently in?
34 What age group likes it the most?
35 How do you feel about it?
36 Who dislikes it?
37 What time of day is it used?
38 What do I wear when I use it?
39 Why do people use it?
40 With what frequency do most people use it?
41 What part of the body do I use it with?
42 What do I clean it with?
43 How is it transported?
44 Where is it stored?
45 Where do you use it?
46 What holiday is it most associated with?
47 What would I use to write on it?
48 What's a game that it appears in?
49 What climate is it most associated with?
50 What website can I find it on?
51 How long will it last if left alone?
52 How does it affect the things around it?
53 Where would I get one?
54 How would it react to being pushed down a hill?
55 What happens if I light it on fire?
56 What happens if I put it under water?
57 What superpower would this thing want?
58 How would I use it as a weapon?
59 How do I feel after using it?
60 What would happen if I bent it?
61 What would I use to destroy it?
62 What's inside it?
63 What's its opposite?
64 What profession works with it?
65 If it were a musical instrument, what instrument would it be?
66 If it was a social media website, what site would it be?
67 What genre of movie/book is most likely to feature it?
68 If it had a favorite food, what would it be?
69 What's something you can make using it?
70 What powers it?
71 What container would I keep it in?
72 What's the secondary material it's made of? (not its main material)
73 Who or what can lift it?
74 What ancient Greek/Roman god is it most associated with?
75 What habitat would I be most like to find it in?
76 What is a group of them called?
77 Who or what makes it?
78 What field of science studies it?
79 Where does it go when it dies, breaks, or is no longer useful?
80 What superhero is it most related to?
81 What month would I find it or use it in?
82 If it was an alcoholic drink, what would it be?

View file

@ -40,20 +40,16 @@ export default (io: Server, socket: Socket, data: JoinGame) => {
game.log.silly(`${host.name} is one of the team's guessers`); game.log.silly(`${host.name} is one of the team's guessers`);
hand = team.hand; hand = team.hand;
team.guessers.push(host); team.guessers.push(host);
socket.join([
`${game.id}:*:guesser`,
`${game.id}:${team.id}:guesser`
]);
break; break;
case "writer": case "writer":
game.log.silly(`${host.name} is the team's writer`); game.log.silly(`${host.name} is the team's writer`);
team.writer = host; team.writer = host;
socket.join([
`${game.id}:*:writer`,
`${game.id}:${team.id}:writer`
]);
break; break;
}; };
socket.join([
`${game.id}:*:${host.role}`,
`${game.id}:${host.team}:${host.role}`
]);
game.log.debug(`Host assigned to team`); game.log.debug(`Host assigned to team`);
}; };
@ -103,14 +99,20 @@ export default (io: Server, socket: Socket, data: JoinGame) => {
if (!sameName.socket?.connected) { if (!sameName.socket?.connected) {
sameName.socket = socket; sameName.socket = socket;
let rooms: string[] = [game.id];
game.log.info(`Player Reconnected to the game (name=${data.name})`); game.log.info(`Player Reconnected to the game (name=${data.name})`);
// Get the hand of the player's team // Get the hand of the player's team
let hand: string[] = []; let hand: string[] = [];
if (sameName.team && sameName.role == `guesser`) { if (sameName.team && sameName.role == `guesser`) {
hand = game.teams[sameName.team - 1].hand; hand = game.teams[sameName.team - 1].hand;
rooms.push(
`${game.id}:*:${sameName.role}`,
`${game.id}:${sameName.team}:${sameName.role}`
);
}; };
socket.join(rooms);
socket.emit(`GameRejoined`, { socket.emit(`GameRejoined`, {
status: 200, status: 200,
ingame: game.ingame, ingame: game.ingame,

View file

@ -29,7 +29,7 @@ export default (io: Server, socket: Socket, data: SelectObject) => {
game.log.debug(`Object has been chosen: ${data.choice}`); game.log.debug(`Object has been chosen: ${data.choice}`);
game.object = data.choice; game.object = data.choice;
io.to(`${game.id}:*:writer`).emit(`ChosenObject`, { io.to(game.id).emit(`ChosenObject`, {
status: 200, status: 200,
choice: data.choice, choice: data.choice,
}); });

View file

@ -25,6 +25,7 @@ export var hibernatedGames: string[] = [];
export var games: {[index: string]: Game} = {}; export var games: {[index: string]: Game} = {};
export const log: Logger = new Logger({ export const log: Logger = new Logger({
displayDateTime: conf.log.datetime,
displayFunctionName: false, displayFunctionName: false,
displayLoggerName: true, displayLoggerName: true,
displayFilePath: `hidden`, displayFilePath: `hidden`,

View file

@ -1,6 +1,7 @@
interface config { interface config {
log: { log: {
level: `silly` | `debug` | `info` | `error` | `warn` | `fatal` | `trace`; level: `silly` | `debug` | `info` | `error` | `warn` | `fatal` | `trace`;
datetime: boolean;
}; };
websocket: { websocket: {
port: number; port: number;

View file

@ -47,18 +47,10 @@ column = 0
fingerprint = '' fingerprint = ''
[webserver]
# whether or not to enable the integrated webserver.
enabled = false
# The port the web server should run on
port = 8080
[websocket] [websocket]
# The port the websocket server should run on. This must also be duplicated # The port the websocket server should run on. This is the port that you will
# into the `main.js` file within the `web` folder so that the client attempts # need to target for the web server's reverse proxy, or the specific URI in the
# to connect to the correct port on the server. # web code itself.
port = 8081 port = 8081
# The hostnames (and protocols) that are allowed to connect to the server. For # The hostnames (and protocols) that are allowed to connect to the server. For
@ -71,6 +63,21 @@ port = 8081
permitted_hosts = "http://localhost:8080" permitted_hosts = "http://localhost:8080"
[datastores]
# Whether or not to save games that are active to the server's hard drive when
# it crashes for whatever reason. This will also enable loading of those games
# on server startup and allows game clients to reconnect to the game.
enabled = false
# The file extension used to save and load the datastore files.
filetype = "game"
# The filepath pointing to where to save the datastore files to. This can
# either be a relative or absolute filepath.
directory = ""
[log] [log]
# The log level to output to the CLI, this can be one of the following: # The log level to output to the CLI, this can be one of the following:
# - "silly" # - "silly"
@ -80,4 +87,7 @@ permitted_hosts = "http://localhost:8080"
# - "error" # - "error"
# - "trace" # - "trace"
# any other value will prevent the server from starting at runtime. # any other value will prevent the server from starting at runtime.
level = "silly" level = "info"
# Whether or not the log should output the date and time information in the log
datetime = true

View file

@ -1,19 +1,73 @@
## Project setup # Setup:
``` 1. `cd` into this `web` directory.
pnpm install 2. Run `pnpm install` to install all of the required dependencies for the site.
```
### Compiles and hot-reloads for development Now jump to the [Production](#Production) or [Development](#Development)
``` section and continue the steps there.
pnpm run serve
```
### Compiles and minifies for production ---
```
pnpm run build
```
### Lints and fixes files ## Production
3. Run `pnpm build` to create the folder with all the files for the webserver.
4. Create a symlink pointing to the `dist/` directory that was created in the
last step.
5. Tell your favourite web server (I'd recommend nginx) for serving the files
from that directory.
6. In the webserver of your choice, you must also setup an
`example.com/socket.io` route that reverse proxies the websocket connection
through to the Node.js server. If you do not want to set up this proxy, you can
bypass it by changing the URI in the `serc/main.js` file to have a specific URL and port.
Example: Change
```js
// This is the default value that will require a
// "/socket.io" proxy in your web server.
let websocket_uri = `/`;
``` ```
pnpm run lint to:
```js
// this is the specific domain and port to connect to, as an example on how to
// bypass setting up a reverse proxy.
let websocket_uri = `http://example.com:1234`;
``` ```
7. Once the reverse proxy or specific URI is setup, connect to your website through the appropriate domain and check to see if it worked.
## Development:
3. Make sure the websocket server is started either through a terminal or
through the systemd service on port `8081` for local development. (if you want
to connect to a websocket server that is not on port 8081 for development, you
will need to change the port value in `src/main.js` to the correct port number)
4. Run `pnpm serve`, this will start a local hot-reloading server on
`localhost:8080`. **DO NOT** reverse-proxy this server. It serves files that
are not optimized nor minified.
5. Go to `localhost:8080` in your web browser of choice.
---
# Configuration:
Configuring the site is pretty simple, the most complicated part is already
done because you have a functional web server.
In order to edit any of the following settings, go to `src/store/index.js` and
find the respective value in the section that allows editing. **Important**:
Any values edited in this file will require re-building the site using
`pnpm build`, and the changes will only take place after players refresh their
web browser.
| Variable | Type | Description
| -------- | ---- | -----------
| survey_link | string | This is the link to the survey that will appear in the player's hand once the object has been correctly guessed and in the attributions modal. This can be left empty or `null` without any problems, the buttons will be hidden.
| name* | string | The name of the team.
| icon* | string | The filename of the icon used for the team reminder for the team. This assumes the file is in the `public/assets/` directory.
| eyes* | other | This is an object of numbers used to indicate how many eyes appear on that slot of the game board for the team.
| writer_name | string | How the interface should refer to the players who are receiving cards and writing.
| writer_card_button | string | The text that appears on the cards that are given to the writer by the guessers.
| writer_object_choose_button | string | The text that appears on the object choices for the writers to select one for them to use before being allowed to start giving clues to the guessers.
| guesser_name | string | How the interface should refer to the players who are sending the cards and guessing what the object is.
| guesser_card_button | string | The text that appears on the button for the cards in the guesser's hand in order to give the card to the writer.
| eye_icon | string | The filename of the eye icon. This assumes the file is in the `public/assets/` directory.
| discard_hand_icon | string | The filename of the icon used on the guesser's discard hand button. This assumes the file is in the `public/assets/` directory.
\* _Note_: These exist for teams 1 and 2, they can each have their own values (and it is recommended that they don't have the same values).

View file

@ -12,7 +12,21 @@
> >
<h2 class="centre">Attributions:</h2> <h2 class="centre">Attributions:</h2>
<p class="centre"> <p class="centre">
Made By: Oliver Akins Ghost Writer is designed and created by
<a href="https://resonym.com" target="_blank" rel="noopener">Resonym</a>
<br>
Online Prototype Made By: Oliver Akins (Alkali Metal)
<br>
<a
v-if="$store.state.survey_link"
:href="$store.state.survey_link"
target="_blank"
rel="noopener"
>
<button class="clickable">
Complete The Survey
</button>
</a>
</p> </p>
<hr> <hr>
<p> <p>
@ -77,6 +91,15 @@ export default {
height: 100%; height: 100%;
} }
button {
background-color: var(--background2);
color: var(--background2-text);
border-radius: 7px;
font-size: large;
padding: 10px;
margin: 5px;
}
a, a:visited { a, a:visited {
color: var(--light-font-colour); color: var(--light-font-colour);
} }

View file

@ -62,17 +62,6 @@ export default {
*/ */
this.objects = data.objects; this.objects = data.objects;
}, },
ChosenObject(data) {
/**
* Sent to all clients so that they can set their store data and in
* turn stay synchronized on what object they are trying to get
* their teammate to guess.
*/
if (data.status < 200 || 300 <= data.status) {
this.$emit(`error`, data);
};
this.$store.commit(`setObject`, data.choice);
},
}, },
mounted() { mounted() {
this.getObjects(); this.getObjects();

View file

@ -243,9 +243,9 @@ input[type="text"] {
border-width: 2px; border-width: 2px;
font-size: larger; font-size: larger;
outline: none; outline: none;
margin: 7px 0;
padding: 7px; padding: 7px;
width: 90%; width: 90%;
margin: 0;
} }
input[type="text"]:focus { input[type="text"]:focus {
border-color: var(--board-background-text); border-color: var(--board-background-text);

View file

@ -4,6 +4,16 @@
{{ mostRecentQuestion }} {{ mostRecentQuestion }}
</div> </div>
<div class="flex-center" v-else-if="gameOver"> <div class="flex-center" v-else-if="gameOver">
<a
v-if="$store.state.survery_link"
:href="$store.state.survey_link"
target="_blank"
rel="noopener"
>
<button class="clickable">
Complete The Survey
</button>
</a>
<button <button
class="clickable" class="clickable"
@click.stop="endGame" @click.stop="endGame"
@ -62,9 +72,12 @@ export default {
return this.$store.state.questions; return this.$store.state.questions;
}, },
gameOver() { gameOver() {
let targetAnswer = this.$store.state.chosen_object.toLowerCase()+`.`; if (this.$store.state.chosen_object) {
return this.$store.state.answers.team_1.includes(targetAnswer) let targetAnswer = this.$store.state.chosen_object.toLowerCase()+`.`;
|| this.$store.state.answers.team_2.includes(targetAnswer); return this.$store.state.answers.team_1.includes(targetAnswer)
|| this.$store.state.answers.team_2.includes(targetAnswer);
};
return false;
}, },
}, },
methods: { methods: {
@ -129,6 +142,18 @@ export default {
console.error(`Server returned an unsupported mode: ${data.mode}`); console.error(`Server returned an unsupported mode: ${data.mode}`);
}; };
}, },
GameReset(data) {
if (data.status < 200 || 300 <= data.status) {
return this.$emit(`error`, data);
};
this.$store.commit(`setAnswers`, {
team_1: new Array(8).fill(``),
team_2: new Array(8).fill(``),
});
this.$store.commit(`replaceHand`, []);
this.$store.commit(`setObject`, null);
this.$store.commit(`view`, `lobby`);
},
}, },
} }
</script> </script>

View file

@ -5,6 +5,8 @@ Vue.use(Vuex);
export default new Vuex.Store({ export default new Vuex.Store({
state: { state: {
survey_link: ``,
team_1: { team_1: {
name: `Sun`, name: `Sun`,
icon: `sun.svg`, icon: `sun.svg`,
@ -31,6 +33,7 @@ export default new Vuex.Store({
guesser_name: `Medium`, guesser_name: `Medium`,
guesser_card_button: `Ask Spirit`, guesser_card_button: `Ask Spirit`,
eye_icon: `eye.svg`, eye_icon: `eye.svg`,
discard_hand_icon: `trash.svg`, discard_hand_icon: `trash.svg`,

View file

@ -48,6 +48,19 @@ export default {
}, },
}, },
methods: {}, methods: {},
sockets: {
ChosenObject(data) {
/**
* Sent to all clients so that they can set their store data and in
* turn stay synchronized on what object they are trying to get
* their teammate to guess.
*/
if (data.status < 200 || 300 <= data.status) {
this.$emit(`error`, data);
};
this.$store.commit(`setObject`, data.choice);
},
}
} }
</script> </script>