-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #14 from cygni/web-facelift
Web facelift
- Loading branch information
Showing
53 changed files
with
3,044 additions
and
2,546 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
NODE_ENV=development | ||
API_URL=http://localhost:8080 | ||
API_URL=http://localhost:8080 | ||
PORT=8090 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,26 +1,45 @@ | ||
# Snakebot Reactclient | ||
|
||
This is the webclient for the Cygni snakebot tournament written in React with TypeScript. This application communicates with a [snakebot game server](https://github.com/cygni/snakebot) using a websocket. | ||
|
||
# | ||
|
||
## Users | ||
|
||
If you are a user who **only** wants to code your own bot, then simply head to the [snakebot client respository](https://github.com/cygni/snakebot-client-js) and follow the instructions there. There will be a *docker-compose* file there to easily get your own server and webclient running as containers without the need to clone them from here. | ||
If you are a user who **only** wants to code your own bot, then simply head to the [snakebot client respository](https://github.com/cygni/snakebot-client-js) and follow the instructions there. There will be a _docker-compose_ file there to easily get your own server and webclient running as containers without the need to clone them from here. | ||
|
||
# | ||
|
||
## Maintainers | ||
|
||
### Requirements | ||
* Node.js >= 16.15.1 | ||
* npm >= 8.11.0 | ||
|
||
- Node.js >= 16.15.1 | ||
- npm >= 8.11.0 | ||
|
||
# | ||
|
||
### To get the development server running locally | ||
|
||
After cloning the repository, open a terminal inside the root folder and run the following commands: | ||
|
||
``` | ||
> npm install | ||
> npm start | ||
``` | ||
|
||
The server should now be running on http://localhost:8090. | ||
|
||
# | ||
|
||
### **Updates and Docker** | ||
|
||
**IMPORTANT**: Commits on the **main** branch will launch an action that builds and **overrides** the docker image tagged as the latest on [DockerHub](https://hub.docker.com/repository/docker/cygni/snakebot-reactclient). Therefore it is important to **ONLY** push changes to **main** that works and have been tested, to ensure that latest image works for anyone who wants to use it. If a commit is deemed **stable** you can also add a **tag** to that commit to ensure it remains on [DockerHub](https://hub.docker.com/repository/docker/cygni/snakebot-reactclient) without getting overwritten. E.g creating a release with a tag will both push the newly build docker image with the tag latest **AND** the tag name given as long as it follows the standard **X.X.X** name. | ||
|
||
Because of what mentioned above, when adding a new feature or changing some behavior, make sure to work on a **different branch** first before pushing to **main**. | ||
|
||
### **Production** | ||
|
||
**IMPORTANT**: Commits to the main branch will also act as commits towards production. Rebuilding the images on DockerHub through commits from main will cause the production server to reboot with the updated version of the image. | ||
|
||
# |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file not shown.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,120 +1,154 @@ | ||
import axios from "axios"; | ||
import { Console } from "console"; | ||
import SockJS from "sockjs-client"; | ||
import Arbitraryconstants from "./constants/Arbitraryconstants"; | ||
import MessageTypes, { GameSettings } from "./constants/messageTypes" | ||
import { onSocketMessage } from "./context/messageDispatch"; | ||
import { GameData } from "./context/slices/gameDataSlice"; | ||
|
||
const socket = new SockJS(Arbitraryconstants.SERVER_URL + "/events"); | ||
import axios from 'axios'; | ||
import SockJS from 'sockjs-client'; | ||
import Arbitraryconstants from './constants/Arbitraryconstants'; | ||
import { GameSettings } from './constants/messageTypes'; | ||
import { onSocketMessage } from './context/messageDispatch'; | ||
import { GameData } from './context/slices/gameDataSlice'; | ||
import { clearTournament } from './context/slices/tournamentSlice'; | ||
import { store } from './context/store'; | ||
|
||
let socket = new SockJS(Arbitraryconstants.SERVER_URL + '/events'); | ||
let onConnectQueue: string[] = []; | ||
newConnection(); | ||
|
||
function newConnection() { | ||
socket = new SockJS(Arbitraryconstants.SERVER_URL + '/events'); | ||
|
||
socket.onopen = () => { | ||
console.log('Connected to server'); | ||
// Send all queued messages | ||
onConnectQueue.forEach((msg) => { | ||
socket.send(msg); | ||
}); | ||
onConnectQueue = []; | ||
}; | ||
|
||
socket.onmessage = (event: any) => onSocketMessage(event.data); | ||
|
||
socket.onclose = () => { | ||
console.log('Disconnected from server'); | ||
localStorage.clear(); | ||
store.dispatch(clearTournament()); | ||
|
||
setTimeout(() => { | ||
console.log('Trying to reconnect...'); | ||
newConnection(); | ||
}, 3000); | ||
}; | ||
} | ||
|
||
function sendWhenConnected(msg: string) { | ||
console.log("Queuing/sending socket message:", JSON.parse(msg)); | ||
if (socket.readyState === 1 && localStorage.getItem("token") !== null) { | ||
socket.send(msg); | ||
} else { | ||
onConnectQueue.push(msg); // if no connection is established, save message in queue | ||
} | ||
console.log('Queuing/sending socket message:', JSON.parse(msg)); | ||
if (socket.readyState === 1 && localStorage.getItem('token') !== null) { | ||
socket.send(msg); | ||
} else { | ||
onConnectQueue.push(msg); // if no connection is established, save message in queue | ||
} | ||
} | ||
|
||
// ############################################################################################## | ||
// ########################### REST API ######################################################## | ||
// ############################################################################################## | ||
export async function getToken(username: string, password: string): Promise<{ success: boolean; data: string }> { | ||
try { | ||
let resp = await axios.get(`/login?login=${username}&password=${password}`); | ||
return { success: true, data: resp.data }; | ||
} catch (error: any) { | ||
console.error('Error getting token:', error); | ||
return { success: false, data: typeof error.response.data === 'string' ? error.response.data : error.message }; | ||
} | ||
} | ||
|
||
export async function getToken(username: string, password: string): Promise<{success: boolean, data: string}> { | ||
try { | ||
let resp = await axios.get(`/login?login=${username}&password=${password}`) | ||
return {success: true, data: resp.data}; | ||
} catch (error: any) { | ||
console.error("Error getting token:", error); | ||
return {success: false, data: typeof(error.response.data)==="string" ? error.response.data : error.message}; | ||
} | ||
export type Game = { | ||
gameDate: string; | ||
gameId: string; | ||
players: string[]; | ||
}; | ||
|
||
async function searchForGames(snakeName: string): Promise<Game[]> { | ||
const resp = await axios.get(`/history/search/${snakeName}`).catch((err) => { | ||
console.error(err); | ||
}); | ||
return resp ? resp.data.items : []; | ||
} | ||
|
||
async function getGame(gameId: string): Promise<GameData> { | ||
const resp = await axios.get(`/history/${gameId}`).catch((err) => { | ||
console.error(err); | ||
}); | ||
return resp ? resp.data : {}; | ||
} | ||
|
||
socket.onopen = () => { | ||
console.log("Connected to server"); | ||
// Send all queued messages | ||
onConnectQueue.forEach(msg => { | ||
socket.send(msg); | ||
}); | ||
onConnectQueue = []; | ||
// ############################################################################################## | ||
// ###################### SOCKET FUNCTIONS ##################################################### | ||
// ############################################################################################## | ||
async function createTournament(tournamentName: string): Promise<void> { | ||
sendWhenConnected( | ||
JSON.stringify({ | ||
type: 'se.cygni.snake.eventapi.request.CreateTournament', | ||
token: localStorage.getItem('token'), | ||
tournamentName: tournamentName, | ||
}) | ||
); | ||
} | ||
|
||
socket.onmessage = (event: any) => onSocketMessage(event.data); | ||
async function killTournament(): Promise<void> { | ||
sendWhenConnected( | ||
JSON.stringify({ | ||
type: 'se.cygni.snake.eventapi.request.KillTournament', | ||
token: localStorage.getItem('token'), | ||
tournamentId: 'NOT_IMPLEMENTED', | ||
}) | ||
); | ||
} | ||
|
||
socket.onclose = () => { | ||
console.log("Disconnected from server"); | ||
async function getActiveTournament(): Promise<void> { | ||
sendWhenConnected( | ||
JSON.stringify({ | ||
type: 'se.cygni.snake.eventapi.request.GetActiveTournament', | ||
token: localStorage.getItem('token'), | ||
}) | ||
); | ||
} | ||
|
||
export type Game = { | ||
gameDate: string, | ||
gameId: string, | ||
players: string[], | ||
async function startTournament(tournamentId: string): Promise<void> { | ||
sendWhenConnected( | ||
JSON.stringify({ | ||
type: 'se.cygni.snake.eventapi.request.StartTournament', | ||
token: localStorage.getItem('token'), | ||
tournamentId: tournamentId, | ||
}) | ||
); | ||
} | ||
|
||
async function startTournamentGame(gameId: string): Promise<void> { | ||
sendWhenConnected( | ||
JSON.stringify({ | ||
type: 'se.cygni.snake.eventapi.request.StartGame', | ||
gameId: gameId, | ||
}) | ||
); | ||
} | ||
|
||
async function updateTournamentSettings(gameSettings: GameSettings): Promise<void> { | ||
sendWhenConnected( | ||
JSON.stringify({ | ||
type: 'se.cygni.snake.eventapi.request.UpdateTournamentSettings', | ||
token: localStorage.getItem('token'), | ||
gameSettings: gameSettings, | ||
}) | ||
); | ||
} | ||
|
||
export default { | ||
|
||
// ############################################################################################## | ||
// ########################### REST API ######################################################## | ||
// ############################################################################################## | ||
async searchForGames(snakeName: string): Promise<Game[]> { | ||
const resp = await axios.get(`/history/search/${snakeName}`).catch(err => { | ||
console.error(err); | ||
}); | ||
return resp ? resp.data.items: []; | ||
}, | ||
|
||
async getGame(gameId: string): Promise<GameData> { | ||
const resp = await axios.get(`/history/${gameId}`).catch(err => { | ||
console.error(err); | ||
}); | ||
return resp ? resp.data: {}; | ||
}, | ||
|
||
// ############################################################################################## | ||
// ###################### SOCKET FUNCTIONS ##################################################### | ||
// ############################################################################################## | ||
async createTournament(tournamentName: string): Promise<void> { | ||
sendWhenConnected(JSON.stringify({ | ||
type: 'se.cygni.snake.eventapi.request.CreateTournament', | ||
token: localStorage.getItem("token"), | ||
tournamentName: tournamentName, | ||
})); | ||
}, | ||
|
||
async killTournament(): Promise<void> { | ||
sendWhenConnected(JSON.stringify({ | ||
type: 'se.cygni.snake.eventapi.request.KillTournament', | ||
token: localStorage.getItem("token"), | ||
tournamentId: 'NOT_IMPLEMENTED', | ||
})); | ||
}, | ||
|
||
async getActiveTournament(): Promise<void> { | ||
sendWhenConnected(JSON.stringify({ | ||
type: 'se.cygni.snake.eventapi.request.GetActiveTournament', | ||
token: localStorage.getItem("token"), | ||
})); | ||
}, | ||
|
||
async startTournament(tournamentId: string): Promise<void> { | ||
sendWhenConnected(JSON.stringify({ | ||
type: 'se.cygni.snake.eventapi.request.StartTournament', | ||
token: localStorage.getItem("token"), | ||
tournamentId: tournamentId, | ||
})); | ||
}, | ||
|
||
async startTournamentGame(gameId: string): Promise<void> { | ||
sendWhenConnected(JSON.stringify({ | ||
type: 'se.cygni.snake.eventapi.request.StartGame', | ||
gameId: gameId, | ||
})); | ||
}, | ||
|
||
async updateTournamentSettings(gameSettings: GameSettings): Promise<void> { | ||
sendWhenConnected(JSON.stringify({ | ||
type: 'se.cygni.snake.eventapi.request.UpdateTournamentSettings', | ||
token: localStorage.getItem("token"), | ||
gameSettings: gameSettings, | ||
})); | ||
}, | ||
}; | ||
const api = { | ||
searchForGames, | ||
getGame, | ||
createTournament, | ||
killTournament, | ||
getActiveTournament, | ||
startTournament, | ||
startTournamentGame, | ||
updateTournamentSettings, | ||
}; | ||
|
||
export default api; |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Oops, something went wrong.