initial commit

main
Tylan Tyson 2 years ago
commit 984f4a3ca5

5
.gitignore vendored

@ -0,0 +1,5 @@
.DS_STORE
.env
venv/
.next/
node_modules/

@ -0,0 +1,314 @@
# Imports
from engineio.payload import Payload
from AesEverywhere import aes256
import flask_socketio
import threading
import requests
import random
import pygame
import flask
import time
import math
import os
from dotenv import load_dotenv
load_dotenv()
# Config
Payload.max_decode_packets = 500
webserver_address = "http://localhost:3000"
cryptography_password = os.environ.get("CRYPTOGRAPHY_PASSWORD")
clock = pygame.time.Clock()
tile_map = [[8, 80, 80, 160, 160], [1, 160, 80, 240, 160], [1, 240, 80, 320, 160], [1, 320, 80, 400, 160], [1, 400, 80, 480, 160], [1, 480, 80, 560, 160], [1, 560, 80, 640, 160], [1, 640, 80, 720, 160], [1, 720, 80, 800, 160], [1, 800, 80, 880, 160], [1, 880, 80, 960, 160], [1, 960, 80, 1040, 160], [1, 1040, 80, 1120, 160], [1, 1120, 80, 1200, 160], [1, 1200, 80, 1280, 160], [1, 1280, 80, 1360, 160], [1, 1360, 80, 1440, 160], [1, 1440, 80, 1520, 160], [1, 1520, 80, 1600, 160], [1, 1600, 80, 1680, 160], [1, 1680, 80, 1760, 160], [1, 1760, 80, 1840, 160], [1, 1840, 80, 1920, 160], [1, 1920, 80, 2000, 160], [1, 2000, 80, 2080, 160], [2, 2080, 80, 2160, 160], [7, 80, 160, 160, 240], [3, 2080, 160, 2160, 240], [7, 80, 240, 160, 320], [3, 2080, 240, 2160, 320], [7, 80, 320, 160, 400], [3, 2080, 320, 2160, 400], [7, 80, 400, 160, 480], [3, 2080, 400, 2160, 480], [7, 80, 480, 160, 560], [5, 160, 480, 240, 560], [5, 240, 480, 320, 560], [5, 320, 480, 400, 560], [5, 400, 480, 480, 560], [5, 480, 480, 560, 560], [3, 2080, 480, 2160, 560], [7, 80, 560, 160, 640], [5, 880, 560, 960, 640], [5, 1520, 560, 1600, 640], [5, 1600, 560, 1680, 640], [5, 1680, 560, 1760, 640], [5, 1760, 560, 1840, 640], [3, 2080, 560, 2160, 640], [7, 80, 640, 160, 720], [3, 2080, 640, 2160, 720], [7, 80, 720, 160, 800], [5, 800, 720, 880, 800], [5, 1200, 720, 1280, 800], [3, 2080, 720, 2160, 800], [7, 80, 800, 160, 880], [3, 2080, 800, 2160, 880], [7, 80, 880, 160, 960], [3, 560, 880, 640, 960], [5, 1040, 880, 1120, 960], [5, 1360, 880, 1440, 960], [3, 2080, 880, 2160, 960], [7, 80, 960, 160, 1040], [3, 560, 960, 640, 1040], [5, 1280, 960, 1360, 1040], [5, 1440, 960, 1520, 1040], [5, 1520, 960, 1600, 1040], [5, 1600, 960, 1680, 1040], [5, 1680, 960, 1760, 1040], [3, 2080, 960, 2160, 1040], [7, 80, 1040, 160, 1120], [5, 480, 1040, 560, 1120], [5, 1760, 1040, 1840, 1120], [3, 2080, 1040, 2160, 1120], [7, 80, 1120, 160, 1200], [5, 1120, 1120, 1200, 1200], [3, 2080, 1120, 2160, 1200], [6, 80, 1200, 160, 1280], [5, 160, 1200, 240, 1280], [5, 240, 1200, 320, 1280], [5, 320, 1200, 400, 1280], [5, 400, 1200, 480, 1280], [5, 480, 1200, 560, 1280], [5, 560, 1200, 640, 1280], [5, 640, 1200, 720, 1280], [5, 720, 1200, 800, 1280], [5, 800, 1200, 880, 1280], [5, 880, 1200, 960, 1280], [5, 960, 1200, 1040, 1280], [5, 1040, 1200, 1120, 1280], [5, 1120, 1200, 1200, 1280], [5, 1200, 1200, 1280, 1280], [5, 1280, 1200, 1360, 1280], [5, 1360, 1200, 1440, 1280], [5, 1440, 1200, 1520, 1280], [5, 1520, 1200, 1600, 1280], [5, 1600, 1200, 1680, 1280], [5, 1680, 1200, 1760, 1280], [5, 1760, 1200, 1840, 1280], [5, 1840, 1200, 1920, 1280], [5, 1920, 1200, 2000, 1280], [5, 2000, 1200, 2080, 1280], [4, 2080, 1200, 2160, 1280]]
users = []
user_positions = [0, 0, 0, 0, 0, 0]
tiles = []
bullets = []
health = []
directions = []
# Cryptography
class Cryptography ():
# Encrypt
@staticmethod
def encrypt ( data, password ):
return aes256.encrypt(data, password)
# Decrypt
@staticmethod
def decrypt ( data, password ):
return aes256.decrypt(data, password)
# Register Server
requests.post(webserver_address + "/api/registerServer", data = Cryptography.encrypt(f"{requests.get('https://api.ipify.org').text},6", cryptography_password))
# Get Bullet Vector
def getBulletVector ( playerX, playerY, clickX, clickY ):
return [ clickX - playerX, clickY - playerY ]
# Get Bullet Normalisation Value
def getBulletNormalisationValue ( distanceX, distanceY ):
return 1 / math.sqrt((distanceX ** 2) + (distanceY ** 2))
# Get Normalised Bullet Vector
def getNormalisedBulletVector ( bulletVector, bulletNormalisationValue ):
return [ bulletVector[0] * bulletNormalisationValue, bulletVector[1] * bulletNormalisationValue ]
# Creates Tiles
for tile in tile_map:
tiles.append(pygame.Rect(tile[1], tile[2],80, 80))
# User
class User ():
# Constructor
def __init__ ( self, socketId, user, gameRank, elo, kills, deaths, player ):
self.socketId = socketId
self.user = user
self.gameRank = gameRank
self.elo = elo
self.kills = kills
self.deaths = deaths
self.new_kills = 0;
self.new_deaths = 0;
self.angle = 0;
self.inputs = []
self.player = player
# Player
class Player(object):
# Constructor
def __init__ ( self ):
self.reset(random.randint(160, 1760), 160)
self.game_over = False
self.image_index = 0
# Update
def update( self, inputs, tiles, dt ):
# Move
dx = 0
dy = 0
# Jump
self.landed = False
if self.jl == True:
self.jl = False
if inputs[0] and self.jumped == False and self.in_air == False:
self.vel_y = -22
self.jumped = True
self.jl = True
self.touchfloor = False
elif inputs[0] == False:
self.jumped = False
# Left
if inputs[1]:
dx -= 7 * dt
self.direction = "left"
# Right
if inputs[3]:
dx += 7 *dt
self.direction = "right"
# Collisions
self.in_air = True
# Direction
if dx < 0:
self.dir = -1
elif dx > 0:
self.dir = 1
# Gravity
self.vel_y += 1 * dt
if self.vel_y > 10:
self.vel_y = 10 *dt
dy += self.vel_y
for tile in tiles:
# Vertical Collisions
if tile.colliderect(self.rect.x, self.rect.y + dy, self.width, self.height):
if self.vel_y >= 0:
dy = tile.top - self.rect.bottom
self.in_air = False
if self.touchfloor == False:
self.touchfloor = True
self.landed = True
elif self.vel_y < 0:
dy = tile.bottom - self.rect.top
self.vel_y = 0
self.rect.y += dy
for tile in tiles:
# X and Y
if tile.colliderect(self.rect.x + dx, self.rect.y + 1, self.width, self.height - 2):
print("true")
if dx >= 0:
dx = tile.left - self.rect.right
elif dx < 0:
dx = tile.right - self.rect.left
self.rect.x += dx
# Update Cords
self.ht = dy
# Return Cords
return self.rect.x, self.rect.y
# Reset
def reset(self, x,y):
self.width = 80
self.height = 80
self.rect = pygame.Rect(x,y,80,80)
self.direction = 0
self.vel_y = 0
self.jumped = False
self.in_air = True
self.health = 8
self.dir = 1
self.touchfloor = False
self.jl = False
self.landed = False
self.ht = 0
# App
app = flask.Flask(__name__)
socketio = flask_socketio.SocketIO(app, cors_allowed_origins = webserver_address)
# Socketio Authentication
@socketio.on("authentication")
def authentication(code):
code = str(Cryptography.decrypt(code.split("ShootyArenaGame=")[1], cryptography_password))[2:-1]
code = code.split(",")
for user in users:
if user.user == code[0]:
socketio.emit("disc", to = flask.request.sid)
return
users.append(User(flask.request.sid, code[0], code[1], int(code[2]), int(code[3]), int(code[4]), Player()))
socketio.emit("map", tile_map)
socketio.emit("player", code[0], to = flask.request.sid)
# Move
@socketio.on("move")
def move(movement):
for user in users:
if flask.request.sid == user.socketId:
user.inputs = movement
# MMove
@socketio.on("mmove")
def mmove(angle):
for user in users:
if flask.request.sid == user.socketId:
try:
user.angle = int(angle["angle"])
except:
user.angle = 0
# Click
@socketio.on("click")
def cli(inputs):
for user in users:
if flask.request.sid == user.socketId:
vec = [inputs["dx"], inputs["dy"]]
nvec = getBulletNormalisationValue(vec[0], vec[1])
fvec = getNormalisedBulletVector(vec, nvec)
inputs["px"] += fvec[0] * 72
inputs["py"] += fvec[1] * 72
hit = False
for tile in tile_map:
if (inputs["px"] + 5 > tile[1] and inputs["px"] + 5 < tile[3]) and (inputs["py"] + 5 > tile[2] and inputs["py"] + 5 < tile[4]):
hit = True
if hit == False:
bullets.append([user.user, inputs["px"], inputs["py"], fvec[0], fvec[1]])
# Game Loop
last_time = time.time()
def game (last_time):
while True:
dt = time.time() - last_time
dt *= 60
last_time = time.time()
health = []
# Bullets
for bulletnum, bullet in enumerate(bullets):
bullet[1] += bullet[3] * 12 * dt
bullet[2] += bullet[4] * 12 * dt
for tile in tile_map:
if (bullet[1] + 5 > tile[1] and bullet[1] + 5 < tile[3]) and (bullet[2] + 5 > tile[2] and bullet[2] + 5 < tile[4]):
del bullets[bulletnum]
# Users
for user in users:
if (bullet[1] + 5 > user.player.rect.x and bullet[1] +5 < user.player.rect.x + 80) and (bullet[2] +5 > user.player.rect.y and bullet[2] +5 < user.player.rect.y + 80):
if bullet[0] != user.user:
del bullets[bulletnum]
user.player.health -= 1
if user.player.health == 0:
user.new_deaths += 1
socketio.emit("die", to = user.socketId)
user.player.reset(random.randint(160, 1760), 160)
for user2 in users:
if bullet[0] == user2.user:
user2.new_kills += 1
directions = []
angles = []
# Users
for usernum, user in enumerate(users):
if len(user.inputs) != 0:
user_positions[usernum] = [user.user, user.player.update(user.inputs, tiles, dt)]
if user.player.jl == True:
socketio.emit("ju", to = user.socketId)
if user.player.landed == True:
socketio.emit("la", to = user.socketId)
directions.append([user.user, user.player.dir])
health.append([user.user, user.player.health])
angles.append([user.user, user.angle])
socketio.emit("users", user_positions)
socketio.emit("directions", directions)
socketio.emit("health", health)
socketio.emit("bullets", bullets)
socketio.emit("ang", angles)
clock.tick(60)
# Run Game Loop
thread = threading.Thread(target=game, args = (last_time,))
thread.start()
# Factorial
def factorial ( number ):
if number == 0:
return 1
else:
return number * factorial(number - 1)
# Calculate Elo Change
def calculateEloChange ( kills, deaths ):
sc = kills - deaths
if sc >= 2:
return factorial(sc) // factorial(sc - 2)
else:
return ( kills - deaths ) * 10
# Calculate Rank
def calculateRank ( elo ):
if elo < 0:
return "Unranked"
elif elo < 1000:
return "Shitty Shooty"
elif elo < 2000:
return "Cutey Shooty"
elif elo < 3000:
return "Beauty Shooty"
else:
return "Snooty Shooty"
# Dissconnect
@socketio.on("disconnect")
def dissconnect ():
for usernum, user in enumerate(users):
if flask.request.sid == user.socketId:
data = aes256.encrypt(f"{user.user},{user.elo+calculateEloChange(user.new_kills, user.new_deaths)},{calculateRank(user.elo + calculateEloChange(user.new_kills, user.new_deaths))},{user.kills + user.new_kills},{user.deaths + user.new_deaths}" , cryptography_password)
requests.post(f"{webserver_address}/api/updateStats", data = data)
del users[usernum]
user_positions[usernum] = 0
# Run Socketio
if __name__ == "__main__":
socketio.run(app, debug = False)

@ -0,0 +1,32 @@
# Tiles
tiles = [
[ 8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2 ],
[ 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3 ],
[ 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3 ],
[ 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3 ],
[ 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3 ],
[ 7, 5, 5, 5, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3 ],
[ 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 0, 0, 0, 3 ],
[ 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3 ],
[ 7, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3 ],
[ 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3 ],
[ 7, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 5, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 3 ],
[ 7, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 5, 5, 5, 5, 0, 0, 0, 0, 3 ],
[ 7, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 3 ],
[ 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3 ],
[ 6 ,5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4 ]
]
# Genrate Tile Map
tile_map = []
y_count = 0
for y in tiles:
y_count += 1
x_count = 0
for x in tiles[y_count - 1]:
x_count += 1
if tiles[y_count - 1][x_count - 1]:
tile_map.append([x, x_count * 80, y_count * 80, ( x_count * 80 ) + 80, ( y_count * 80 ) + 80 ])
# Display Tile Map
print(tile_map)

@ -0,0 +1,24 @@
aes-everywhere==1.2.10
bidict==0.22.1
certifi==2022.12.7
charset-normalizer==3.1.0
click==8.1.3
Flask==2.2.3
Flask-SocketIO==5.3.3
h11==0.14.0
idna==3.4
importlib-metadata==6.6.0
itsdangerous==2.1.2
Jinja2==3.1.2
MarkupSafe==2.1.2
pycryptodomex==3.17
pygame==2.3.0
python-dotenv==1.0.0
python-engineio==4.4.1
python-socketio==5.8.0
requests==2.28.2
simple-websocket==0.10.0
urllib3==1.26.15
Werkzeug==2.2.3
wsproto==1.2.0
zipp==3.15.0

@ -0,0 +1,15 @@
# Imports
import random
# Key
key = ""
# Characters
characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"
# Generate Key
for i in range(32):
key += characters[ random.randint(0, 61) ]
# Display Key
print(key)

@ -0,0 +1,31 @@
#!/usr/bin/env python3
import requests
import base64
from Crypto.Cipher import AES
MODE = AES.MODE_CFB
BLOCK_SIZE = 16
SEGMENT_SIZE = 128
def _pad_string(value):
length = len(value)
pad_size = BLOCK_SIZE - (length % BLOCK_SIZE)
return value.ljust(length + pad_size, '\x00')
def encrypt(key, iv, plaintext):
aes = AES.new(key, MODE, iv, segment_size=SEGMENT_SIZE)
plaintext = _pad_string(plaintext)
encrypted_text = aes.encrypt(plaintext)
return encrypted_text
key = 'TfvY7I358yospfWKcoviZizOShpm5hyH'
iv = 'mb13KcoviZizvYhp'
original_message = 'hi'
encryptedpayload = base64.b64encode(encrypt(key, iv, original_message))
request = requests.post("http://localhost:8080/hello", encryptedpayload)
print(request.content)

@ -0,0 +1,51 @@
// Strict Mode
"use strict";
// Imports
import cookie from "./cookie/cookie.js";
import jsonwebtoken from "./jsonwebtoken/jsonwebtoken.js";
// Key
const JsonwebtokenKey = process.env.JSON_WEB_TOKEN_KEY;
// Set User
async function setUser ( req, res, data ) {
cookie.set(req, res, "ShootyArenaUser", jsonwebtoken.set(data, JsonwebtokenKey), true);
};
// Get User
async function getUser ( req, res ) {
return new Promise (async (resolve) => {
try {
const userCookie = cookie.get(req, res, "ShootyArenaUser");
if (userCookie === undefined) {
setUser(req, res, "");
resolve("");
} else {
const user = await jsonwebtoken.get(userCookie, JsonwebtokenKey);
if (user === false || user.data === "") {
setUser(req, res, "");
resolve("");
} else {
setUser(req, res, user.data);
resolve(user.data);
};
};
} catch {
resolve("")
}
});
};
// Set Game
async function setGame ( req, res, data ) {
cookie.set(req, res, "ShootyArenaGame", data, false);
};
// Exports
module.exports = {
setUser,
getUser,
setGame
};

@ -0,0 +1,25 @@
// Strict Mode
"use strict";
// Imports
import cookies from "cookies";
// Set
function set ( req, res, name, data, httpOnly ) {
new cookies(req, res).set(name, data, {
secure: false,
httpOnly: httpOnly,
sameSite: true
});
};
// Get
function get ( req, res, name ) {
return new cookies(req, res).get(name);
};
// Exports
module.exports = {
set,
get
};

@ -0,0 +1,26 @@
// Strict Mode
"use strict";
// Imports
import jsonwebtoken from "jsonwebtoken";
// Set
function set ( data, key ) {
return JSON.stringify(jsonwebtoken.sign({ data: data }, key, { expiresIn: "30m" }));
};
// Get
function get ( data, key ) {
return new Promise ((resolve) => {
jsonwebtoken.verify(JSON.parse(data), key, (error, data) => {
if (error) resolve(false);
resolve(data);
});
});
};
// Exports
module.exports = {
set,
get
};

@ -0,0 +1,14 @@
// Strict Mode
"use strict";
// Styles
import styles from "./copyrightCard.module.css";
// Copyright Card
export default function CopyrightCard () {
return (
<div className = { styles.copyrightCard }>
<h6>Copyright - Shooty Arena</h6>
</div>
);
};

@ -0,0 +1,17 @@
/* Copyright Card */
.copyrightCard {
width: 100vw;
padding-bottom: 20px;
display: flex;
justify-content: center;
align-items: center;
background-color: #1a1a1a;
}
/* Copyright Card H6 */
.copyrightCard h6 {
font-family: NeonSans;
font-size: 10px;
color: #f700ff;
text-shadow: 0 0 10px #f700ff, 0 0 20px #f700ff, 0 0 30px #f700ff, 0 0 40px #f700ff;
}

@ -0,0 +1,24 @@
// Strict Mode
"use strict";
// Styles
import styles from "./footer.module.css";
// Components
import CopyrightCard from "./copyrightCard/copyrightCard.jsx";
import LinkCard from "./linkCard/linkCard";
import RenewableCard from "./renewableCard/renewableCard.jsx";
// Footer
export default function Footer () {
return (
<div className = { styles.footer }>
<div>
<RenewableCard/>
<LinkCard title = "Legal" links = { [["Privacy Policy", "./privacypolicy"], ["Terms and Conditions", "./termsandconditions"], ["Accessibility Statement", "./accessibilitystatement"]] }/>
<LinkCard title = "Other" links = { [["Contact", "./contact"]] }/>
</div>
<CopyrightCard/>
</div>
);
};

@ -0,0 +1,27 @@
/* Footer */
.footer {
width: 100vw;
display: flex;
flex-direction: column;
justify-content: center;
align-items: flex-start;
background-color: #1a1a1a;
}
/* Footer Div First Child */
.footer div:first-child {
display: flex;
flex-wrap: wrap;
}
/* Small Screens */
@media screen and (max-width: 779px) {
/* Footer */
.footer {
align-items: center;
}
/* Footer Div First Child */
.footer div:first-child {
width: 260px;
}
}

@ -0,0 +1,17 @@
// Strict Mode
"use strict";
// Styles
import styles from "./linkCard.module.css";
// Link Card
export default function LinkCard ({ title, links }) {
return (
<div className = { styles.linkCard }>
<h5>{ title }</h5>
{
links.map(link => <h5><a href = { link[1] }>{ link[0] }</a></h5>)
}
</div>
);
};

@ -0,0 +1,43 @@
/* Link Card */
.linkCard {
width: 200px;
height: 100px;
padding: 30px;
background-color: #1a1a1a;
}
/* Link Card H5 */
.linkCard h5 {
width: 100%;
padding-top: 12px;
font-family: NeonSans;
font-size: 12px;
color: #ff9d00;
text-shadow: 0 0 10px #ff9d00, 0 0 20px #ff9d00, 0 0 30px #ff9d00, 0 0 40px #ff9d00;
transition: text-shadow 0.2s linear;
}
/* Link Card H5 Hover */
.linkCard h5:hover {
text-shadow: 0 0 10px #ff9d00, 0 0 20px #ff9d00;
}
/* Link Card H5 First Child */
.linkCard h5:first-child {
padding-top: 0;
font-size: 18px;
color: #8c00ff;
text-shadow: 0 0 10px #8c00ff, 0 0 20px #8c00ff, 0 0 30px #8c00ff, 0 0 40px #8c00ff;
}
/* Link Card A */
.linkCard a {
color: #ff9d00;
}
/* Small Screens */
@media screen and (max-width: 779px) {
/* Legal Card H5 */
.linkCard h5 {
text-align: center;
}
}

@ -0,0 +1,14 @@
// Strict Mode
"use strict";
// Styles
import styles from "./renewableCard.module.css";
// Renewable Card
export default function RenewableCard () {
return (
<div className = { styles.renewableCard }>
<h4>Powered by renewable energy.</h4>
</div>
);
};

@ -0,0 +1,28 @@
/* Renewable Card Text Flicker */
@keyframes renewableCardTextFlicker {
30%, 32%, 34%, 80% {
text-shadow: 0 0 10px #00ff2a, 0 0 20px #00ff2a, 0 0 30px #00ff2a, 0 0 40px #00ff2a;
}
0%, 31%, 33%, 35%, 81% {
text-shadow: 0 0 10px #00ff2a, 0 0 20px #00ff2a;
}
}
/* Renewable Card */
.renewableCard {
width: 200px;
padding: 30px;
background-color: #1a1a1a;
}
/* Renewable Card H4 */
.renewableCard h4 {
width: 100%;
height: 100%;
text-align: center;
font-family: NeonSans;
font-size: 30px;
color: #00ff2a;
text-shadow: 0 0 10px #00ff2a, 0 0 20px #00ff2a, 0 0 30px #00ff2a, 0 0 40px #00ff2a;
animation: renewableCardTextFlicker 6s infinite;
}

@ -0,0 +1,19 @@
// Strict Mode
"use strict";
// Styles
import styles from "./header.module.css";
// Components
import NavigationCard from "./navigationCard/navigationCard.jsx";
import TitleCard from "./titleCard/titleCard.jsx";
// Header
export default function Header () {
return (
<div className = { styles.header }>
<TitleCard/>
<NavigationCard/>
</div>
);
};

@ -0,0 +1,23 @@
/* Header */
.header {
display: flex;
justify-content: center;
align-items: center;
background-color: #1a1a1a;
flex-wrap: wrap;
}
/* Header A */
.header a {
font-family: neon;
text-decoration: none;
padding: 20px;
}
/* Small Screens */
@media screen and (max-width: 1027px) {
/* Header */
.header {
flex-direction: column;
}
}

@ -0,0 +1,16 @@
// Strict Mode
"use strict";
// Styles
import styles from "./navigationCard.module.css";
// Navigation Card
export default function NavigationCard () {
return (
<div className = { styles.navigationCard }>
<h5><a href = "/play">Play</a></h5>
<h5><a href = "/leaderboards/1">Leaderboards</a></h5>
<h5><a href = "/account">Account</a></h5>
</div>
);
};

@ -0,0 +1,35 @@
/* Navigation Card */
.navigationCard {
width: 40%;
padding: 30px;
display: flex;
justify-content: flex-start;
align-items: center;
flex-wrap: wrap;
background-color: #1a1a1a;
}
/* Navigation Card H5 A */
.navigationCard h5 a {
font-family: NeonSans;
font-size: 20px;
color: #ff9d00;
text-shadow: 0 0 10px #ff9d00, 0 0 20px #ff9d00, 0 0 30px #ff9d00, 0 0 40px #ff9d00;
transition: text-shadow 0.2s linear;
}
/* Navigation Card H5 A Hover */
.navigationCard h5 a:hover {
text-shadow: 0 0 10px #ff9d00, 0 0 20px #ff9d00;
transition: text-shadow 0.2s linear;
}
/* Small Screens */
@media screen and (max-width: 1027px) {
/* Navigation Card */
.navigationCard {
width: 200px;
padding-top: 0;
justify-content: center;
}
}

@ -0,0 +1,15 @@
// Strict Mode
"use strict";
// Styles
import styles from "./titleCard.module.css";
// Title Card
export default function TitleCard () {
return (
<div className = { styles.titleCard }>
<h4><a href = "/">Shooty</a></h4>
<h4><a href = "/">Arena</a></h4>
</div>
);
};

@ -0,0 +1,56 @@
/* Title Card Shooty Text Flicker */
@keyframes titleCardShootyTextFlicker {
37%, 39%, 41%, 43% {
text-shadow: 0 0 10px #0062ff, 0 0 20px #0062ff, 0 0 30px #0062ff, 0 0 40px #0062ff;
}
35%, 38%, 40%, 42% {
text-shadow: 0 0 10px #0062ff, 0 0 20px #0062ff;
}
}
/* Title Card Arena Text Flicker */
@keyframes titleCardArenaTextFlicker {
38%, 40% {
text-shadow: 0 0 10px #ff3300, 0 0 20px #ff3300, 0 0 30px #ff3300, 0 0 40px #ff3300;
}
35%, 39% {
text-shadow: 0 0 10px #ff3300, 0 0 20px #ff3300;
}
}
/* Title Card */
.titleCard {
width: 40%;
padding: 30px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
background-color: #1a1a1a;
}
/* Title Card H4 First Child */
.titleCard h4:first-child {
text-shadow: 0 0 10px #0062ff, 0 0 20px #0062ff, 0 0 30px #0062ff, 0 0 40px #0062ff;
animation: titleCardShootyTextFlicker 4s infinite;
}
/* Title Card h4 Last Child */
.titleCard h4:last-child {
text-shadow: 0 0 10px #ff3300, 0 0 20px #ff3300, 0 0 30px #ff3300, 0 0 40px #ff3300;
animation: titleCardArenaTextFlicker 6s infinite;
}
/* Title Card H4 First Child A */
.titleCard h4:first-child a {
font-family: NeonSans;
font-size: 50px;
color: #0062ff;
}
/* Title Card H4 Last Child A */
.titleCard h4:last-child a {
font-family: NeonSans;
font-size: 50px;
color: #ff3300;
}

@ -0,0 +1,14 @@
// Strict Mode
"use strict";
// Styles
import styles from "./title.module.css";
// Title
export default function Title ( props ) {
return (
<div className = { styles.title }>
<h1>{ props.title }</h1>
</div>
);
};

@ -0,0 +1,27 @@
/* Title Text Flicker */
@keyframes titleTextFlicker {
44%, 48%, 50% {
text-shadow: 0 0 10px #00ff2a, 0 0 20px #00ff2a, 0 0 30px #00ff2a, 0 0 40px #00ff2a;
}
42%, 46%, 49% {
text-shadow: 0 0 10px #00ff2a, 0 0 20px #00ff2a;
}
}
/* Title */
.title {
padding: 30px;
display: flex;
justify-content: center;
align-items: center;
background-color: #1a1a1a;
}
/* Title H1 */
.title h1 {
font-family: NeonSans;
font-size: 70px;
color: #00ff2a;
text-shadow: 0 0 10px #00ff2a, 0 0 20px #00ff2a, 0 0 30px #00ff2a, 0 0 40px #00ff2a;
animation: titleTextFlicker 4s infinite;
}

@ -0,0 +1,14 @@
// Strict Mode
"use strict";
// Imports
import encrypt from "./encrypt/encrypt.js";
import hash from "./hash/hash.js";
// Exports
module.exports = {
encrypt: encrypt.encrypt,
decrypt: encrypt.decrypt,
hash: hash.hash,
compareHash: hash.compareHash
};

@ -0,0 +1,24 @@
// Strict Mode
"use strict";
// Imports
import aesEverywhere from "aes-everywhere";
// Encrypt Key
const encryptKey = process.env.CRYPTOGRAPHY_PASSWORD;
// Encrypt
async function encrypt ( data ) {
return aesEverywhere.encrypt(data, encryptKey);
};
// Decrypt
async function decrypt ( data ) {
return aesEverywhere.decrypt(data, encryptKey);
};
// Exports
module.exports = {
encrypt,
decrypt
};

@ -0,0 +1,21 @@
// Strict Mode
"use strict";
// Imports
import bcrypt from "bcrypt";
// Hash
async function hash ( data ) {
return bcrypt.hash(data, await bcrypt.genSalt(8));
};
// Compare Hash
async function compareHash ( data, hash ) {
return bcrypt.compare(data, hash);
};
// Exports
module.exports = {
hash,
compareHash
};

@ -0,0 +1,368 @@
// Strict Mode
"use strict";
// Imports
import mysql from "mysql";
// Connection
const connection = mysql.createConnection({
host: "127.0.0.1",
user: process.env.DATABASE_USER,
password: process.env.DATABASE_PASSWORD,
database: "shootyarena"
});
// Connect
connection.connect(( error ) => {
console.log(error)
if (error) throw error;
});
// Create Tables
async function createTables () {
connection.query("CREATE table users ( username VARCHAR(10) NOT NULL UNIQUE PRIMARY KEY, email VARCHAR(331) NOT NULL UNIQUE, passphrase VARCHAR(60) NOT NULL, active VARCHAR(60) NOT NULL, lastLoginTime INT NOT NULL );", ( error, _ ) => {
if (error) throw error;
});
connection.query("CREATE TABLE stats ( username VARCHAR(10) NOT NULL UNIQUE PRIMARY KEY, FOREIGN KEY(username) REFERENCES users(username), gameRank VARCHAR(13) NOT NULL, elo INT NOT NULL, kills INT NOT NULL, deaths INT NOT NULL );", ( error, _ ) => {
if (error) throw error;
});
connection.query("CREATE TABLE servers ( address VARCHAR(45) UNIQUE, playerLimit INT NOT NULL, playerCount INT NOT NULL );", ( error, _ ) => {
if (error) throw error;
});
connection.query("CREATE TABLE codes ( username VARCHAR(10) NOT NULL UNIQUE PRIMARY KEY, FOREIGN KEY(username) REFERENCES users(username), code INT NOT NULL );", ( error, _ ) => {
if (error) throw error;
})
};
// Drop Tables
async function dropTables () {
connection.query("DROP TABLE servers;", ( error, _ ) => {
if (error) throw error;
});
connection.query("DROP TABLE stats;", ( error, _ ) => {
if (error) throw error;
});
connection.query("DROP TABLE codes;", ( error, _ ) => {
if (error) throw error;
});
connection.query("DROP TABLE users;", ( error, _ ) => {
if (error) throw error;
});
};
// Create User
async function createUser ( username, email, passphrase, active ) {
return new Promise (async ( resolve, _ ) => {
connection.query("INSERT INTO users ( username, email, passphrase, active, lastLoginTime ) VALUES ?;", [[[ username, email, passphrase, active, 0 ]]], ( error, _ ) => {
if (error) {
resolve(false);
} else {
resolve(true);
};
});
});
};
// Create Stat
async function createStat ( username ) {
return new Promise (async ( resolve, _ ) => {
connection.query("INSERT INTO stats ( username, gameRank, elo, kills, deaths ) VALUES ?;", [[[ username, "Shitty Shooty", 800, 0, 0 ]]], ( error, _ ) => {
if (error) {
resolve(false);
} else {
resolve(true);
};
});
});
};
// Create Server
async function createServer ( address, playerLimit ) {
return new Promise (async ( resolve, _ ) => {
connection.query("INSERT INTO servers ( address, playerLimit, playerCount ) VALUES ?;", [[[ address, playerLimit, 0 ]]], ( error, _ ) => {
if (error) {
resolve(false);
} else {
resolve(true);
};
});
});
};
// Create Code
async function createCode ( username, code ) {
return new Promise (async ( resolve, _ ) => {
connection.query("INSERT INTO codes ( username, code ) VALUES ?;", [[[ username, code ]]], ( error, _ ) => {
if (error) {
resolve(false);
} else {
resolve(true);
}
})
})
}
// Delete Server
async function deleteServer ( address ) {
return new Promise (async ( resolve, _ ) => {
connection.query("DELETE FROM servers WHERE address = ?;", [[ address ]], ( error, _ ) => {
if (error) {
resolve(false);
} else {
resolve(true);
};
});
});
};
// Username Exists
async function usernameExists ( username ) {
return new Promise (async ( resolve, _ ) => {
connection.query("SELECT username FROM users WHERE username = ?;", [[ username ]], ( error, result ) => {
console.log(error, result)
if (error || result[0] !== undefined) {
resolve(true);
} else {
resolve(false);
};
});
});
};
// Email Exists
async function emailExists ( email ) {
return new Promise (async ( resolve, _ ) => {
connection.query("SELECT email FROM users WHERE email = ?;", [[ email ]], ( error, result ) => {
if (error || result[0] !== undefined) {
resolve(true);
} else {
resolve(false);
};
});
});
};
// Active Exists
async function activeExists ( active ) {
return new Promise (async ( resolve, _ ) => {
connection.query("SELECT active FROM users WHERE active = ?;", [[ active ]], ( error, result ) => {
if (error || result[0] !== undefined) {
resolve(true);
} else {
resolve(false);
};
});
});
};
// Activate User
async function activateUser ( active ) {
return new Promise (async ( resolve, _ ) => {
connection.query("UPDATE users SET active = '' WHERE active = ?;", [[ active ]], ( error, result ) => {
if (error || result.affectedRows === 0) {
resolve(false);
} else {
resolve(true);
};
});
});
};
// Login User
async function loginUser ( email ) {
return new Promise (async ( resolve, _ ) => {
connection.query("SELECT passphrase FROM users WHERE email = ?;", [[ email ]], ( error, result ) => {
if (error || result[0] === undefined) {
resolve(false);
} else {
resolve(result[0]);
};
});
});
};
// Get All Stats
async function getAllStats () {
return new Promise (async ( resolve, _ ) => {
connection.query("SELECT * FROM stats ORDER BY elo DESC;", ( error, result ) => {
if (error) {
resolve(false);
} else {
resolve(result);
};
});
});
};
// Get Stats
async function getStats ( username ) {
return new Promise (async ( resolve, _ ) => {
connection.query("SELECT gameRank, elo, kills, deaths FROM stats WHERE username = ?;", [[ username ]], ( error, result ) => {
if (error || result[0] === undefined) {
resolve(false);
} else {
resolve(result);
};
});
});
};
// Update Game Rank
async function updateGameRank ( username, gameRank ) {
return new Promise (async ( resolve, _ ) => {
connection.query("UPDATE stats SET gameRank = ? WHERE username = ?;", [[ gameRank ], [ username ]], ( error, result ) => {
if (error || result.affectedRows === 0) {
resolve(false);
} else {
resolve(true);
};
});
});
};
// Update Elo
async function updateElo ( username, elo ) {
return new Promise (async ( resolve, _ ) => {
connection.query("UPDATE stats SET elo = ? WHERE username = ?;", [[ elo ], [ username ]], ( error, result ) => {
if (error || result.affectedRows === 0) {
resolve(false);
} else {
resolve(true);
};
});
});
};
// Update Kills
async function updateKills ( username, kills ) {
return new Promise (async ( resolve, _ ) => {
connection.query("UPDATE stats SET kills = ? WHERE username = ?;", [[ kills ], [ username ]], ( error, result ) => {
if (error || result.affectedRows === 0) {
resolve(false);
} else {
resolve(true);
};
});
});
};
// Update Deaths
async function updateDeaths ( username, deaths ) {
return new Promise (async ( resolve, _ ) => {
connection.query("UPDATE stats SET deaths = ? WHERE username = ?;", [[ deaths ], [ username ]], ( error, result ) => {
if (error || result.affectedRows === 0) {
resolve(false);
} else {
resolve(true);
};
});
});
};
// Get Free Server
async function getFreeServer () {
return new Promise (async ( resolve, _ ) => {
connection.query("SELECT address FROM servers WHERE playerCount < playerLimit ORDER BY playerCount DESC;", ( error, result ) => {
if (error || result[0] === undefined) {
resolve(false);
} else {
resolve(result[0]);
};
});
});
};
// Set Player Count
async function setPlayerCount ( address, playerCount ) {
return new Promise (async ( resolve, _ ) => {
connection.query("UPDATE servers SET playerCount = ? WHERE address = ?;", [[ playerCount ], [ address ]], ( error, _ ) => {
if (error || result.affectedRows === 0) {
resolve(false);
} else {
resolve(true);
};
});
});
};
// Get Active
async function getActive ( email ) {
return new Promise (async ( resolve, _ ) => {
connection.query("SELECT active FROM users WHERE email = ?;", [[email]], ( error, result ) => {
if (error == null && result[0].active == "") {
resolve(true);
} else {
resolve(result[0].active);
}
});
});
};
// Get Username
async function getUsername ( email ) {
return new Promise (async ( resolve, _ ) => {
connection.query("SELECT username FROM users WHERE email = ?;", [[email]], ( error, result ) => {
if (error || result[0] === undefined) {
resolve(false);
} else {
resolve(result[0].username)
}
})
})
}
// Get Code
async function getCode ( username ) {
return new Promise (async ( resolve, _ ) => {
connection.query("SELECT code FROM codes WHERE username = ?;", [[ username ]], ( error, result ) => {
if (error || result[0] === undefined) {
resolve(false);
} else {
resolve(result[0].code);
}
})
})
}
// Set Code
async function setCode ( username, code ) {
return new Promise (async ( resolve, _ ) => {
connection.query("UPDATE codes SET code = ? WHERE username = ?", [[code], [username]], ( error, result ) => {
if (error) {
resolve(false);
} else {
resolve(true);
}
})
})
}
// Exports
module.exports = {
createTables,
dropTables,
createUser,
createStat,
createServer,
deleteServer,
usernameExists,
emailExists,
activeExists,
activateUser,
loginUser,
getAllStats,
getStats,
updateGameRank,
updateElo,
updateKills,
updateDeaths,
getFreeServer,
setPlayerCount,
getActive,
getUsername,
createCode,
getCode,
setCode,
};

@ -0,0 +1,12 @@
// Strict Mode
"use strict";
// Imports
import validate from "./validate/validate.js"
import send from "./send/send.js";
// Exports
module.exports = {
validateEmail: validate.validateEmail,
sendAccountActivationEmail: send.sendAccountActivationEmail
};

@ -0,0 +1,41 @@
// Strict Mode
"use strict";
// Imports
import nodemailer from "nodemailer";
// Transport
const transport = nodemailer.createTransport({
host: process.env.MAIL_HOST,
port: 587,
secure: process.env.MAIL_SECURE != "false",
auth: {
user: process.env.MAIL_USER,
pass: process.env.MAIL_PASS
}
});
// Send Account Activation Email
async function sendAccountActivationEmail ( to, link ) {
return new Promise (async ( resolve, _ ) => {
const mailOptions = {
from: "Shooty Arena",
to: to,
subject: "Shooty Arena Account Activation",
text: "Welcome to Shooty Arena! To activate your account visit { link }".replace("{ link }", link)
};
transport.sendMail(mailOptions, ( err, _ ) => {
if (err) {
console.log("mail error")
console.log(err)
resolve(false)
};
resolve(true);
});
});
};
// Exports
module.exports = {
sendAccountActivationEmail
};

@ -0,0 +1,17 @@
// Strict Mode
"use strict";
// Imports
const deepEmailValidator = require("deep-email-validator");
// Validate Email
async function validateEmail ( email ) {
const check = await deepEmailValidator.validate(email);
if (check.validators.regex.valid && check.validators.mx.valid) return true;
return false;
};
// Exports
module.exports = {
validateEmail
};

@ -0,0 +1,7 @@
// Strict Mode
"use strict";
// Exports
module.exports = {
reactStrictMode: true
};

15968
web/package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -0,0 +1,28 @@
{
"name": "web",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"aes-everywhere": "^1.0.0",
"bcrypt": "^5.0.1",
"config": "^3.3.7",
"cookies": "^0.8.0",
"deep-email-validator": "^0.1.21",
"jsonwebtoken": "^8.5.1",
"mysql": "^2.18.1",
"next": "^12.0.10",
"nodemailer": "^6.7.2",
"react": "^17.0.2",
"react-dom": "17.0.2",
"socket.io-client": "^4.4.1"
},
"devDependencies": {
"eslint": "8.6.0",
"eslint-config-next": "12.0.7"
}
}

@ -0,0 +1,22 @@
// Strict Mode
"use strict";
// Imports
import styles from "../styles/404.module.css";
import Header from "../components/header/header.jsx";
import Title from "../components/title/title.jsx";
import Footer from "../components/footer/footer.jsx";
// Error 404
export default function Error404 () {
return (
<div>
<Header/>
<Title title = "Error"/>
<div className = { styles.error404 }>
<h2>404</h2>
</div>
<Footer/>
</div>
);
};

@ -0,0 +1,13 @@
// Strict Mode
"use strict";
// Styles
import "../styles/reset.css"
import "../styles/fonts.css"
// My App
export default function MyApp ( { Component, pageProps } ) {
return (
<Component { ...pageProps }/>
);
};

@ -0,0 +1,18 @@
// Strict Mode
"use strict";
// Imports
import Header from "../components/header/header.jsx";
import Title from "../components/title/title.jsx";
import Footer from "../components/footer/footer.jsx";
// Index
export default function Index () {
return (
<div>
<Header/>
<Title title = "505 - Error"/>
<Footer/>
</div>
);
};

@ -0,0 +1,163 @@
// Strict Mode
"use strict";
// Imports
import React, { useEffect, useState } from "react";
import authentication from "../authentication/authentication.js";
import styles from "../styles/account.module.css";
import Header from "../components/header/header.jsx";
import Title from "../components/title/title.jsx";
import Footer from "../components/footer/footer.jsx";
// Get Server Side Props
export async function getServerSideProps ( ctx ) {
try {
const user = await authentication.getUser(ctx.req, ctx.res);
if (user == "") {
return {
props: {
loggedIn: false
}
};
} else {
return {
props: {
loggedIn: true
}
};
};
} catch {
return {
props: {
loggedIn: false
}
};
}
};
// Account
export default function Home( props ) {
const [ signInError, setSignInError ] = useState("");
const [ signUpError, setSignUpError ] = useState("");
useEffect(() => {
const host = "http://localhost:3000/";
try {
const signInForm = document.querySelector(".signInForm");
signInForm.addEventListener("submit", async function ( event ) {
try {
event.preventDefault();
const formData = new FormData(signInForm).entries();
const request = await fetch(host + "api/signIn", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(Object.fromEntries(formData))
});
const result = await request.json();
if (result.response.success == true && result.response.activated == true) {
window.location = host + "account";
} else if (result.response.success == true && result.response.activated == false) {
setSignInError("Account Not Activated");
} else {
setSignInError("Bad Login");
};
} catch (error) {
console.log(error)
setSignInError("Error");
};
}, []);
} catch {};
try {
const signUpForm = document.querySelector(".signUpForm");
signUpForm.addEventListener("submit", async function ( event ) {
try {
event.preventDefault();
const formData = new FormData(signUpForm).entries();
const response = await fetch(host + "api/createAccount", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(Object.fromEntries(formData))
});
const result = await response.json();
if (result.response.error != "") {
setSignUpError(result.response.error);
} else {
setSignUpError("Check Your Email To Activate Your Account");
}
} catch (error) {
setSignUpError("Error");
console.log(error)
};
});
} catch {};
try {
const signOutForm = document.querySelector(".signOutForm");
signOutForm.addEventListener("submit", async function ( event ) {
try {
event.preventDefault();
const formData = new FormData(signOutForm).entries()
const response = await fetch(host + "api/signOut", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(Object.fromEntries(formData))
});
await response.json();
window.location = host + "account";
} catch {
window.location = host + "account";
};
}, []);
} catch {};
});
return (
<div>
<Header/>
<Title title = "Account"/>
{
props.loggedIn ?
<div className = { styles.account }>
<form className = "signOutForm">
<button type = "submit">Sign Out</button>
</form>
</div>
: (
<div className = { styles.account }>
<div>
<h2>Sign In</h2>
<form className = "signInForm">
<input type = "email" placeholder = "Email" name = "Email"/>
<input type = "password" placeholder = "Password" name = "Password"/>
<button type = "submit">Submit</button>
</form>
{
signInError ?
<h3>{ signInError }</h3>
: (
<h3>&nbsp;</h3>
)
}
</div>
<div>
<h2>Sign Up</h2>
<form className = "signUpForm">
<input type = "text" placeholder = "Username" name = "Username"/>
<input type = "email" placeholder = "Email" name = "Email"/>
<input type = "password" placeholder = "Password" name = "Password"/>
<input type = "password" placeholder = "Confirm Password" name = "Confirm Password"/>
<button type = "submit">Submit</button>
</form>
{
signUpError ?
<h3>{ signUpError }</h3>
: (
<h3>&nbsp;</h3>
)
}
</div>
</div>
)
}
<Footer/>
</div>
);
};

@ -0,0 +1,164 @@
// Strict Mode
"use strict";
// Imports
import React, { useEffect, useState } from "react";
import authentication from "../../authentication/authentication.js";
import database from "../../database/database.js";
import styles from "../../styles/account.module.css";
import Header from "../../components/header/header.jsx";
import Title from "../../components/title/title.jsx";
import Footer from "../../components/footer/footer.jsx";
// Get Server Side Props
export async function getServerSideProps ( ctx ) {
try {
if (await database.activateUser(ctx.query.activate)) {
await authentication.getUser(ctx.req, ctx.res);
return {
props: {
message: "Account Activated"
}
};
} else {
await authentication.getUser(ctx.req, ctx.res);
return {
props: {
message: "Error Activating Account"
}
};
};
} catch {
await authentication.getUser(ctx.req, ctx.res);
return {
props: {
message: "Error"
}
};
};
};
// Account
export default function Home( props ) {
const [ signInError, setSignInError ] = useState("");
const [ signUpError, setSignUpError ] = useState("");
useEffect(() => {
const host = "http://localhost:3000/";
try {
const signInForm = document.querySelector(".signInForm");
signInForm.addEventListener("submit", async function ( event ) {
try {
event.preventDefault();
const formData = new FormData(signInForm).entries();
const request = await fetch(host + "api/signIn", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(Object.fromEntries(formData))
});
const result = await request.json();
if (result.response.success == true && result.response.activated == true) {
window.location = host + "account";
} else if (result.response.success == true && result.response.activated == false) {
setSignInError("Account Not Activated");
} else {
setSignInError("Bad Login");
};
} catch {
setSignInError("Error");
};
}, []);
} catch {};
try {
const signUpForm = document.querySelector(".signUpForm");
signUpForm.addEventListener("submit", async function ( event ) {
try {
event.preventDefault();
const formData = new FormData(signUpForm).entries();
const response = await fetch(host + "api/createAccount", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(Object.fromEntries(formData))
});
const result = await response.json();
if (result.response.error != undefined) {
setSignUpError(result.response.error);
} else {
setSignUpError("Check Your Email To Activate Your Account");
}
} catch {
setSignUpError("Error");
};
});
} catch {};
try {
const signOutForm = document.querySelector(".signOutForm");
signOutForm.addEventListener("submit", async function ( event ) {
try {
event.preventDefault();
const formData = new FormData(signOutForm).entries()
const response = await fetch(host + "api/signOut", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(Object.fromEntries(formData))
});
await response.json();
window.location = host + "account";
} catch {
window.location = host + "account";
};
}, []);
} catch {};
});
return (
<div>
<Header/>
<Title title = "Account"/>
<Title title = { props.message }/>
{
props.loggedIn ?
<div className = { styles.account }>
<form className = "signOutForm">
<button type = "submit">Sign Out</button>
</form>
</div>
: (
<div className = { styles.account }>
<div>
<h2>Sign In</h2>
<form className = "signInForm">
<input type = "email" placeholder = "Email" name = "Email"/>
<input type = "password" placeholder = "Password" name = "Password"/>
<button type = "submit">Submit</button>
</form>
{
signInError ?
<h3>{ signInError }</h3>
: (
<h3>&nbsp;</h3>
)
}
</div>
<div>
<h2>Sign Up</h2>
<form className = "signUpForm">
<input type = "text" placeholder = "Username" name = "Username"/>
<input type = "email" placeholder = "Email" name = "Email"/>
<input type = "password" placeholder = "Password" name = "Password"/>
<input type = "password" placeholder = "Confirm Password" name = "Confirm Password"/>
<button type = "submit">Submit</button>
</form>
{
signUpError ?
<h3>{ signUpError }</h3>
: (
<h3>&nbsp;</h3>
)
}
</div>
</div>
)
}
<Footer/>
</div>
);
};

@ -0,0 +1,74 @@
// Strict Mode
"use strict";
// Imports
import cryptography from "../../cryptography/cryptography.js";
import database from "../../database/database.js";
import mail from "../../mail/mail.js";
// Handler
export default async function handler ( req, res ) {
try {
const host = "http://localhost:3000/";
const response = {
success: false,
error: ""
};
req.body.Username = req.body.Username.toUpperCase();
if (req.body.Username.length < 1) {
response.error = "Enter A Username";
res.send({
response
});
} else if (req.body.Username.length > 10) {
response.error = "Username Must Be 10 Characters Or Less";
res.send({
response
});
} else if (req.body.Username.match(/^\w+$/) == null) {
response.error = "Username Must Only Contain Alphanumberic Characters";
res.send({
response
});
} else if (await database.usernameExists(req.body.Username)) {
response.error = "Username Already In Use";
res.send({
response
});
} else if (await mail.validateEmail(req.body.Email) == false) {
response.error = "Email Is Not Valid";
res.send({
response
});
} else if (await database.emailExists(req.body.Email)) {
response.error = "Email Already In Use";
res.send({
response
});
} else if (req.body.Password.length < 4) {
response.error = "Password Must Be At Least 4 Characters Long";
res.send({
response
});
} else if (await database.createUser(req.body.Username, req.body.Email, await cryptography.hash(req.body.Password), req.body.Username)) {
response.success = true;
const t = await mail.sendAccountActivationEmail(req.body.Email, host + "activate/" + req.body.Username);
console.log(t)
await database.createStat(req.body.Username);
res.send({
response
});
} else {
response.error = "Error Creating User";
res.send({
response
});
};
} catch {
response.success = false;
response.error = "Error";
res.send({
response
});
};
};

@ -0,0 +1,14 @@
// Strict Mode
"use strict";
// Imports
import database from "../../../database/database.js";
// Handler
export default async function handler ( req, res ) {
try {
res.send(await database.getStats(req.query.getStats)[0]);
} catch {
res.send(500);
};
};

@ -0,0 +1,21 @@
// Strict Mode
"use strict";
// Imports
import database from "../../database/database.js";
// Handler
export default async function handler ( req, res ) {
try {
const aes256 = require("aes-everywhere");
const decryptedData = aes256.decrypt(req.body, process.env.CRYPTOGRAPHY_PASSWORD).split(",");
if (decryptedData.length != 2) {
res.send(500);
} else {
await database.createServer(decryptedData[0], decryptedData[1]);
res.send(200);
};
} catch {
res.send(500);
};
};

@ -0,0 +1,14 @@
// Strict Mode
"use strict";
// Imports
import database from "../../database/database.js";
// Handler
export default async function handler ( req, res ) {
try {
res.send(await database.getFreeServer());
} catch {
res.send(500);
};
};

@ -0,0 +1,49 @@
// Strict Mode
"use strict";
// Imports
import authentication from "../../authentication/authentication.js";
import cryptography from "../../cryptography/cryptography.js";
import database from "../../database/database.js";
import mail from "../../mail/mail.js";
// Handler
export default async function handler ( req, res ) {
try {
const response = {
success: false,
activated: false
};
const password = await database.loginUser(req.body.Email);
if (password == false) {
res.send({
response
});
} else {
const active = await database.getActive(req.body.Email);
if (await cryptography.compareHash(req.body.Password, password.passphrase)) {
if (active === true) {
const user = await database.getUsername(req.body.Email);
await authentication.setUser(req, res, user);
response.success = true;
response.activated = true;
res.send({
response
});
} else {
await mail.sendAccountActivationEmail(req.body.Email, "http://localhost:3000/" + "activate/" + await database.getActive(req.body.Email));
response.success = true;
res.send({
response
});
};
} else {
res.send({
response
});
};
};
} catch {
res.send(500);
};
};

@ -0,0 +1,15 @@
// Strict Mode
"use strict";
// Imports
import authentication from "../../authentication/authentication.js";
// Handler
export default async function handler ( req, res ) {
try {
await authentication.setUser(req, res, "");
res.send(200);
} catch {
res.send(500);
};
};

@ -0,0 +1,25 @@
// Strict Mode
"use strict";
// Imports
import database from "../../database/database.js";
// Handler
export default async function handler ( req, res ) {
try {
const aes256 = require("aes-everywhere");
const data = aes256.decrypt(req.body , 'password');
const decryptedData = data.split(",");
if (decryptedData.length != 5) {
res.send(500);
} else {
await database.updateElo(decryptedData[0], decryptedData[1]);
await database.updateGameRank(decryptedData[0], decryptedData[2]);
await database.updateKills(decryptedData[0], decryptedData[3]);
await database.updateDeaths(decryptedData[0], decryptedData[4]);
res.send(200);
};
} catch {
res.send(500);
};
};

@ -0,0 +1,38 @@
// Strict Mode
"use strict";
// Imports
import authentication from "../authentication/authentication.js";
import styles from "../styles/index.module.css";
import Header from "../components/header/header.jsx";
import Title from "../components/title/title.jsx";
import Footer from "../components/footer/footer.jsx";
// Get Server Side Props
export async function getServerSideProps ( ctx ) {
try {
await authentication.getUser(ctx.req, ctx.res);
return {
props: {}
};
} catch {
return {
props: {}
};
}
};
// Index
export default function Index () {
return (
<div>
<Header/>
<Title title = "Home"/>
<div className = { styles.index }>
<h2>Shooty Arena is an online multiplayer platformer shooter! Sign in or create an account to get started.</h2>
<h3>Currently in alpha. V0.1.0</h3>
</div>
<Footer/>
</div>
);
};

@ -0,0 +1,172 @@
// Strict Mode
"use strict";
// Imports
import authentication from "../../authentication/authentication.js";
import database from "../../database/database.js";
import styles from "../../styles/leaderboards.module.css";
import Header from "../../components/header/header.jsx";
import Title from "../../components/title/title.jsx";
import Footer from "../../components/footer/footer.jsx";
// Get Server Side Props
export async function getServerSideProps ( ctx ) {
try {
await authentication.getUser(ctx.req, ctx.res);
const stats = await database.getAllStats();
const pageStats = [];
let previousPage = false;
let nextPage = false;
if (ctx.query.page != 1) {
previousPage = Number(ctx.query.page) - 1;
};
for (let stat = ((ctx.query.page - 1) * 4); stat < stats.length; stat++) {
if (stat <= ((ctx.query.page - 1) * 4) + 3) {
pageStats.push(stats[stat]);
} else {
nextPage = Number(ctx.query.page) + 1;
};
};
if (pageStats.length == 0) {
return {
redirect: {
permanent: false,
destination: "/leaderboards/1"
}
};
};
return {
props: {
pageStats: JSON.stringify(pageStats),
previousPage: previousPage,
nextPage: nextPage
}
};
} catch {
return {
redirect: {
permanent: false,
destination: "/leaderboards/1"
}
};
}
};
// Leaderboard
export default function Leaderboards ( props ) {
return (
<div>
<Header/>
<Title title = "Leaderboards"/>
<div className = { styles.leaderboards }>
<div>
<table>
<tr>
<th>
Username
</th>
<th>
Game Rank
</th>
<th>
Elo
</th>
<th>
Kills
</th>
<th>
Deaths
</th>
</tr>
{
JSON.parse(props.pageStats).map(stats => {
return (
<tr>
<td>
{ stats.username }
</td>
<td>
{ stats.gameRank }
</td>
<td>
{ stats.elo }
</td>
<td>
{ stats.kills }
</td>
<td>
{ stats.deaths }
</td>
</tr>
);
})
}
</table>
<table>
<tr>
<th>
Rank
</th>
<th>
Elo
</th>
</tr>
<tr>
<td>
Shitty Shooty
</td>
<td>
0
</td>
</tr>
<tr>
<td>
Cutie Shooty
</td>
<td>
1000
</td>
</tr>
<tr>
<td>
Beauty Shooty
</td>
<td>
2000
</td>
</tr>
<tr>
<td>
Snooty Shooty
</td>
<td>
3000
</td>
</tr>
</table>
</div>
{
props.previousPage != false && props.nextPage != false &&
<div>
<a href = { "/leaderboards/?".replace("?", props.previousPage) }>Previous Page</a>
<a href = { "/leaderboards/?".replace("?", props.nextPage) }>Next Page</a>
</div>
}
{
props.previousPage != false && props.nextPage == false &&
<div>
<a href = { "/leaderboards/?".replace("?", props.previousPage) } style = {{"text-align": "center"}}>Previous Page</a>
</div>
}
{
props.nextPage != false && props.previousPage == false &&
<div>
<a href = { "/leaderboards/?".replace("?", props.nextPage) } style = {{"text-align": "center"}}>Next Page</a>
</div>
}
</div>
<Footer/>
</div>
);
};

@ -0,0 +1,574 @@
// Strict Mode
"use strict";
// Imports
import React, { useEffect, useState, useRef } from "react";
import authentication from "../authentication/authentication.js";
import database from "../database/database.js";
import styles from "../styles/play.module.css";
import Header from "../components/header/header.jsx";
import Title from "../components/title/title.jsx";
import Footer from "../components/footer/footer.jsx";
import io from "socket.io-client";
// Get Server Side Props
export async function getServerSideProps ( ctx ) {
try {
let user = ""
try {
user = await authentication.getUser(ctx.req, ctx.res)
} catch (e) {
user = ""
}
if (user=="") {
return {
redirect: {
destination: '/account',
permanent: false,
},
}
} else {
var AES256 = require('aes-everywhere');
const stats = await database.getStats(user)
await authentication.setGame(ctx.req, ctx.res, AES256.encrypt(`${user},${stats[0].gameRank},${stats[0].elo},${stats[0].kills},${stats[0].deaths}`, 'passphrase = encrypt("Shooty Arena")'))
return {
props: {
loggedIn: true
}
}
}
} catch {
return {
redirect: {
destination: '/account',
permanent: false,
},
}
}
}
// Play
export default function Play () {
// Refs
const canvasRef = useRef(null);
let socket = null;
let menu = true;
// Use Effect
useEffect(() => {
// Load Images
const image_flat = new Image();
image_flat.src = "/tiles/flat.png";
const image_corner = new Image();
image_corner.src = "/tiles/corner.png";
const image_background = new Image();
image_background.src = "/backgrounds/background.png";
const image_background_light = new Image();
image_background_light.src = "/backgrounds/backgroundLight.png";
const image_player_right = new Image();
image_player_right.src = "/players/right.png";
const image_player_left = new Image();
image_player_left.src = "/players/left.png"
const image_gun_right = new Image();
image_gun_right.src = "/players/gunRight.png";
const image_gun_left = new Image();
image_gun_left.src = "/players/gunLeft.png";
// Sounds
const shotSound1 = new Audio("/sounds/shoot.mp3");
const shotSound2 = new Audio("/sounds/shoot.mp3");
const shotSound3 = new Audio("/sounds/shoot.mp3");
const shotSound4 = new Audio("/sounds/shoot.mp3");
const shotSound5 = new Audio("/sounds/shoot.mp3");
const shotSound6 = new Audio("/sounds/shoot.mp3");
const shotSound7 = new Audio("/sounds/shoot.mp3");
const shotSound8 = new Audio("/sounds/shoot.mp3");
const shotSound9 = new Audio("/sounds/shoot.mp3");
const shotSound10 = new Audio("/sounds/shoot.mp3");
const shotSound11 = new Audio("/sounds/shoot.mp3");
const shotSound12 = new Audio("/sounds/shoot.mp3");
const shotSound13 = new Audio("/sounds/shoot.mp3");
const shotSound14 = new Audio("/sounds/shoot.mp3");
const shotSound15 = new Audio("/sounds/shoot.mp3");
const shotSound16 = new Audio("/sounds/shoot.mp3");
// Jump Sounds
const jumpSound1 = new Audio("/sounds/jump.mp3");
const jumpSound2 = new Audio("/sounds/jump.mp3");
const jumpSound3 = new Audio("/sounds/jump.mp3");
const jumpSound4 = new Audio("/sounds/jump.mp3");
// Jump Sounds
const landSound1 = new Audio("/sounds/land.mp3");
const landSound2 = new Audio("/sounds/land.mp3");
const landSound3 = new Audio("/sounds/land.mp3");
const landSound4 = new Audio("/sounds/land.mp3");
const landSound5 = new Audio("/sounds/land.mp3");
const landSound6 = new Audio("/sounds/land.mp3");
const landSound7 = new Audio("/sounds/land.mp3");
const landSound8 = new Audio("/sounds/land.mp3");
// Die Sound
const dieSound = new Audio("/sounds/die.mp3");
// Button
class Button {
constructor() {
this.startx = 0;
this.starty = 0;
this.x = 400;
this.y = 300;
};
click(canvas, event) {
const rect = canvas.getBoundingClientRect();
const x = event.clientX - rect.left;
const y = event.clientY - rect.top;
if (x > this.startx && x < this.x) {
if (y > this.starty && y < this.y) {
return true;
};
};
return false;
};
};
// Fullscreen
function openFullscreen ( c ) {
if (c.requestFullscreen) {
c.requestFullscreen();
} else if (c.webkitRequestFullscreen) {
c.webkitRequestFullscreen();
} else if (c.msRequestFullscreen) {
c.msRequestFullscreen();
};
};
// Canvas
const canvas = canvasRef.current;
const ctx = canvas.getContext("2d");
ctx.canvas.width = 1920;
ctx.canvas.height = 1080;
// Draw Images
function drawImage( ctx, image, x, y, w, h, degrees ){
ctx.save();
ctx.translate(x+w/2, y+h/2);
ctx.rotate(degrees*Math.PI/180.0);
ctx.translate(-x-w/2, -y-h/2);
ctx.drawImage(image, x, y, w, h);
ctx.restore();
};
function drawImage2( ctx, image, x, y, w, h, degrees ){
ctx.save();
ctx.translate(x, y + 20);
ctx.rotate(degrees*Math.PI/180.0);
ctx.translate(-x, -y - 20);
ctx.drawImage(image, x, y, w, h);
ctx.restore();
};
function drawImage3( ctx, image, x, y, w, h, degrees ){
ctx.save();
ctx.translate(x + w, y + 20);
ctx.rotate(degrees*Math.PI/180.0);
ctx.translate(-x - w, -y - 20);
ctx.drawImage(image, x, y, w, h);
ctx.restore();
};
// Data
const playButton = new Button();
let map = [];
let player;
let players = [];
let bullets = [];
let scroll = [0, 0]
let health = [];
let directions = [];
let angles = [];
let lastClick = [0,0];
let shotnum = 1;
let jc = 1;
let lc = 1;
// Draw Timeout
setInterval(() => {
// Wipe Screen
ctx.fillStyle = "#1c1c1c";
ctx.fillRect(0, 0, 1920 , 1080);
// Scroll
let bakc1 = 160 - scroll[0];
let bakc2 = 160 - scroll[1];
let b1 = scroll[0];
let b2 = scroll[1];
// Backround
ctx.drawImage(image_background, bakc1, bakc2);
// Menu
if (menu == true) {
ctx.fillStyle = "#FF0000";
ctx.fillRect(playButton.startx, playButton.starty, playButton.x, playButton.y);
// Game
} else {
// Map
for (let i = 0; i < map.length; i++) {
ctx.fillStyle = "#FF0000";
if (map[i][0] == 1) {
ctx.drawImage(image_flat, parseInt(map[i][1] - scroll[0], 10), parseInt(map[i][2] - scroll[1], 10));
} else if (map[i][0] == 2) {
ctx.drawImage(image_corner, parseInt(map[i][1] - scroll[0], 10), parseInt(map[i][2] - scroll[1], 10));
} else if (map[i][0] == 3) {
drawImage(ctx, image_flat, parseInt(map[i][1] - scroll[0], 10), parseInt(map[i][2] - scroll[1], 10), 80, 80, 90);
} else if (map[i][0] == 4) {
drawImage(ctx, image_corner, parseInt(map[i][1] - scroll[0], 10), parseInt(map[i][2] - scroll[1], 10), 80, 80, 90);
} else if (map[i][0] == 5) {
drawImage(ctx, image_flat, parseInt(map[i][1] - scroll[0], 10), parseInt(map[i][2] - scroll[1], 10), 80, 80, 180);
} else if (map[i][0] == 6) {
drawImage(ctx, image_corner, parseInt(map[i][1] - scroll[0], 10), parseInt(map[i][2] - scroll[1], 10), 80, 80, 180);
}else if (map[i][0] == 7) {
drawImage(ctx, image_flat, parseInt(map[i][1] - scroll[0], 10), parseInt(map[i][2] - scroll[1], 10), 80, 80, 270);
} else if (map[i][0] == 8) {
drawImage(ctx, image_corner, parseInt(map[i][1] - scroll[0], 10), parseInt(map[i][2] - scroll[1], 10), 80, 80, 270);
};
};
// Bullets
for (let i = 0; i < bullets.length; i++ ) {
ctx.fillStyle = "FF0000";
ctx.fillRect(bullets[i][1] - scroll[0], bullets[i][2] - scroll[1], 10, 10);
};
// Players
for (let i = 0; i < players.length; i++) {
ctx.fillStyle = "00FF00";
for (let y = 0; y < directions.length; y++) {
if (players[i][0] == directions[y][0]) {
if (directions[y][1] < 0) {
ctx.drawImage(image_player_left, players[i][1][0] - b1, players[i][1][1] - b2, 80, 80);
for (let an = 0; an < angles.length; an ++) {
if (angles[an][0] == players[i][0]) {
drawImage3(ctx, image_gun_left, players[i][1][0] - b1 - 45, players[i][1][1] - b2 + 28, 80, 32, 180 + angles[an][1]);
};
};
} else {
ctx.drawImage(image_player_right, players[i][1][0] - b1, players[i][1][1] - b2, 80, 80);
for (let an = 0; an < angles.length; an ++) {
if (angles[an][0] == players[i][0]) {
drawImage2(ctx, image_gun_right, players[i][1][0] - b1 + 35, players[i][1][1] - b2 + 28, 80, 32, angles[an][1]);
};
};
};
};
};
// Scroll
if (players[i] != 0) {
if (players[i][0] == player) {
scroll[0] += (players[i][1][0] - scroll[0] - 900)/20;
scroll[1] += (players[i][1][1] - scroll[1] - 500)/20;
}
}
// Health
for (let x = 0; x < health.length; x ++) {
if (players[i][0] == health[x][0]) {
ctx.fillStyle = "FF0000";
ctx.fillRect(players[i][1][0] - b1, players[i][1][1] - b2 - 40, 80, 10);
ctx.fillStyle = "00FF00";
ctx.fillRect(players[i][1][0] - b1, players[i][1][1] - b2 - 40, health[x][1] * 10, 10);
};
};
};
// Light
ctx.drawImage(image_background_light, bakc1, bakc2)
// Mouse Move
if (socket != null) {
let px, py;
for (let i = 0; i < players.length; i++) {
if (players[i][0] == player) {
px = players[i][1][0] + 40 - scroll[0];
py = players[i][1][1] + 40 - scroll[1];
};
};
let fx = lastClick[0] - px;
let fy = lastClick[1] - py;
let angle = Math.atan(fy/fx);
let deg = (angle * 180) / Math.PI;
if (lastClick[0] < px) deg += 180;
if (deg == null || deg == undefined) deg = 0;
socket.emit("mmove", {
angle: deg
});
};
};
}, 20);
// Inputs
let inputs = [false, false, false, false, [false, 0, 0]];
// Click
function clickLocation( canvas, evt ) {
var rect = canvas.getBoundingClientRect();
let scaleX = canvas.width / rect.width;
let scaleY = canvas.height / rect.height;
return {
x: (evt.clientX - rect.left) * scaleX,
y: (evt.clientY - rect.top) * scaleY
}
}
// Clicks
canvas.addEventListener("mousedown", (event) => {
openFullscreen(canvas)
// No
if (socket == null) {
if (playButton.click(canvas, event)) {
shotSound1.load()
shotSound2.load()
shotSound3.load()
shotSound4.load()
shotSound5.load()
shotSound6.load()
shotSound7.load()
shotSound8.load()
shotSound9.load()
shotSound10.load()
shotSound11.load()
shotSound12.load()
shotSound13.load()
shotSound14.load()
shotSound15.load()
shotSound16.load()
jumpSound1.load()
jumpSound2.load()
jumpSound3.load()
jumpSound4.load()
landSound1.load()
landSound2.load()
landSound3.load()
landSound4.load()
landSound5.load()
landSound6.load()
landSound7.load()
landSound8.load()
dieSound.load()
// Fetch Ip
fetch("http://localhost:3000/api/serverIp").then(res => {return res.json()}).then(data => {
// Game Server
socket = io( "http://localhost" + ":5000/")
menu = false;
// Mouse Move
canvas.addEventListener("mousemove", e => {
let { x,y } = clickLocation(canvas, e);
lastClick = [x,y];
let px, py;
for (let i = 0; i < players.length; i++) {
if (players[i][0] == player) {
px = players[i][1][0] + 40 - scroll[0]
py = players[i][1][1] + 40 - scroll[1]
};
};
let fx = x - px;
let fy = y - py;
let angle = Math.atan(fy/fx);
let deg = (angle * 180) / Math.PI;
if (x < px) deg += 180;
if (deg == null || deg == undefined) deg = 0;
socket.emit("mmove", {
angle: deg
});
});
// Connect
socket.on("connect", () => {
socket.emit("authentication", document.cookie);
});
// Map
socket.on("map", (dat) => {
map = dat;
});
// Users
socket.on("users", (dat) => {
console.log("rec")
players = dat;
});
// Player
socket.on("player", (dat) => {
player = dat;
});
// Bullets
socket.on("bullets", (dat) => {
bullets = dat;
});
// Health
socket.on("health", (data) => {
health = data;
});
// Directions
socket.on("directions", (data) => {
directions = data;
});
// Disc
socket.on("disc", () => {
window.location.reload();
})
// Ang
socket.on("ang", (data) => {
angles = data;
});
// Die
socket.on("die", () => {
dieSound.volume = 0.5;
dieSound.play();
});
// Ju
socket.on("ju", () => {
// Jump Sound
jumpSound1.volume = 0.025;
jumpSound2.volume = 0.025;
jumpSound3.volume = 0.025;
jumpSound4.volume = 0.025;
if (jc == 5) jc = 1;
if (jc == 1) jumpSound1.play();
if (jc == 2) jumpSound2.play();
if (jc == 3) jumpSound3.play();
if (jc == 4) jumpSound4.play();
jc += 1;
});
// La
socket.on("la", () => {
// Volume
landSound1.volume = 0.2
landSound2.volume = 0.2
landSound3.volume = 0.2
landSound4.volume = 0.2
landSound5.volume = 0.2
landSound6.volume = 0.2
landSound7.volume = 0.2
landSound8.volume = 0.2
if (lc == 1) landSound1.play();
if (lc == 2) landSound2.play();
if (lc == 3) landSound3.play();
if (lc == 4) landSound4.play();
if (lc == 5) landSound5.play();
if (lc == 6) landSound6.play();
if (lc == 7) landSound7.play();
if (lc == 8) landSound8.play();
if (lc == 9) lc = 1;
lc += 1;
});
// Inputs
setInterval(()=>{
socket.emit("move", inputs)
}, 2);
});
}
} else {
for (let i = 0; i < players.length; i++) {
if (players[i] != 0) {
if (players[i][0] == player) {
let { x, y } = clickLocation(canvas, event);
let playerX = players[i][1][0] + 40 - scroll[0];
let playerY = players[i][1][1] + 40 - scroll[1];
// Shot Sounds
if (shotnum == 17) shotnum = 1;
if (shotnum == 1) shotSound1.play();
if (shotnum == 2) shotSound2.play();
if (shotnum == 3) shotSound3.play();
if (shotnum == 4) shotSound4.play();
if (shotnum == 5) shotSound5.play();
if (shotnum == 6) shotSound6.play();
if (shotnum == 7) shotSound7.play();
if (shotnum == 8) shotSound8.play();
if (shotnum == 9) shotSound9.play();
if (shotnum == 10) shotSound10.play();
if (shotnum == 11) shotSound11.play();
if (shotnum == 12) shotSound12.play();
if (shotnum == 13) shotSound13.play();
if (shotnum == 14) shotSound14.play();
if (shotnum == 15) shotSound15.play();
if (shotnum == 16) shotSound16.play();
shotnum += 1;
// Click
socket.emit("click", {
clickX: x,
clickY: y,
dx: x - playerX,
dy: y - playerY,
px: players[i][1][0] + 35,
py: players[i][1][1] + 35
});
};
};
};
};
});
// Keyboard
document.addEventListener("keydown", e => {
if (socket != null) {
if (e.key == "w") {
inputs[0] = true;
};
if (e.key == "a") {
inputs[1] = true;
};
if (e.key == "s") {
inputs[2] = true;
};
if (e.key == "d") {
inputs[3] = true;
};
};
});
document.addEventListener("keyup", e => {
if (socket != null) {
if (e.key == "w") {
inputs[0] = false;
};
if (e.key == "a") {
inputs[1] = false;
};
if (e.key == "s") {
inputs[2] = false;
};
if (e.key == "d") {
inputs[3] = false;
};
};
});
}, []);
// Page
return (
<div>
<Header/>
<Title title = "Play"/>
<div className = { styles.play }>
<canvas id="myCanvas" width="1920" height="1080" ref = {canvasRef}></canvas>
</div>
<Footer/>
</div>
);
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

@ -0,0 +1,13 @@
/* Error404 */
.error404 {
background-color: #1a1a1a;
}
/* Error404 H2 */
.error404 h2 {
text-align: center;
font-family: NeonSans;
font-size: 40vw;
color: #8c00ff;
text-shadow: 0 0 4vw #8c00ff, 0 0 6vw #8c00ff;
}

@ -0,0 +1,117 @@
/* Account */
.account {
display: flex;
justify-content: center;
background-color: #1a1a1a;
}
/* Account Div */
.account div {
width: 30%;
padding: 50px;
display: flex;
flex-direction: column;
text-align: center;
}
/* Account Div H2 */
.account div h2 {
padding: 40px;
font-family: NeonSans;
font-size: 25px;
color: #8c00ff;
text-shadow: 0 0 10px #8c00ff, 0 0 20px #8c00ff, 0 0 30px #8c00ff, 0 0 40px #8c00ff;
}
/* Account Div H3 */
.account div h3 {
padding: 10px;
font-family: NeonSans;
font-size: 20px;
background-color: #1a1a1a;
color: #f700ff;
text-shadow: 0 0 10px #f700ff, 0 0 20px #f700ff, 0 0 30px #f700ff, 0 0 40px #f700ff;
}
/* Account Div Form */
.account div form {
display: flex;
flex-direction: column;
}
/* Account Div Form Input */
.account div form input {
padding: 15px;
margin: 15px;
border: 1px solid #f700ff;
border-radius: 5px;
font-family: NeonSans;
font-size: 20px;
background-color: #1a1a1a;
color: #f700ff;
text-shadow: 0 0 10px #f700ff, 0 0 20px #f700ff, 0 0 30px #f700ff, 0 0 40px #f700ff;
}
/* Account Div Form Input Placeholder */
.account div form input::placeholder {
font-family: NeonSans;
color: #f700ff;
}
/* Account Div Form Button */
.account div form button {
padding: 15px;
margin: 15px;
cursor: pointer;
border: 1px solid #ff9d00;
border-radius: 5px;
font-family: NeonSans;
font-size: 20px;
background-color: #1a1a1a;
color: #ff9d00;
text-shadow: 0 0 10px #ff9d00, 0 0 20px #ff9d00, 0 0 30px #ff9d00, 0 0 40px #ff9d00;
transition: text-shadow 0.2s linear;
}
/* Account Div Form Button Hover */
.account div form button:hover {
text-shadow: 0 0 10px #ff9d00, 0 0 20px #ff9d00;
transition: text-shadow 0.2s linear;
}
/* Account Form Button */
.account form button {
padding: 15px;
margin: 15px;
margin-bottom: 30px;
cursor: pointer;
border: 1px solid #ff9d00;
border-radius: 5px;
font-family: NeonSans;
font-size: 20px;
background-color: #1a1a1a;
color: #ff9d00;
text-shadow: 0 0 10px #ff9d00, 0 0 20px #ff9d00, 0 0 30px #ff9d00, 0 0 40px #ff9d00;
transition: text-shadow 0.2s linear;
}
/* Account Form Button Hover */
.account form button:hover {
text-shadow: 0 0 10px #ff9d00, 0 0 20px #ff9d00;
transition: text-shadow 0.2s linear;
}
/* Small Screens */
@media screen and (max-width: 1000px) {
/* Account */
.account {
flex-direction: column;
align-items: center;
}
/* Account Div */
.account div {
width: 80%;
padding-top: 20px;
padding-bottom: 20px;
}
}

@ -0,0 +1,5 @@
/* NeonSans Font Face */
@font-face {
font-family: NeonSans;
src: url(../public/fonts/NeonSans.ttf);
}

@ -0,0 +1,30 @@
/* Index */
.index {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
/* Index H2 */
.index h2 {
width: 80%;
padding: 20px;
text-align: center;
font-family: NeonSans;
font-size: 25px;
color: #8c00ff;
text-shadow: 0 0 10px #8c00ff, 0 0 20px #8c00ff, 0 0 30px #8c00ff, 0 0 40px #8c00ff;
}
/* Index H3 */
.index h3 {
width: 80%;
padding: 20px;
padding-bottom: 40px;
text-align: center;
font-family: NeonSans;
font-size: 15px;
color: #f700ff;
text-shadow: 0 0 10px #f700ff, 0 0 20px #f700ff, 0 0 30px #f700ff, 0 0 40px #f700ff;
}

@ -0,0 +1,93 @@
/* Leaderboards */
.leaderboards {
background-color: #1a1a1a;
}
/* Leaderboards Div First Child */
.leaderboards div:first-child {
padding: 10px 40px;
padding-top: 40px;
display: flex;
justify-content: space-around;
align-items: flex-start;
}
/* Leaderboards Div First Child Table */
.leaderboards div:first-child table {
border-spacing: 20px;
}
/* Leaderboards Div First Child Table First Child */
.leaderboards div:first-child table:first-child {
width: 65%;
}
/* Leaderboards Div First Child Table Last Child */
.leaderboards div:first-child table:last-child {
width: 25%;
}
/* Leaderboards Div First Child Table Tr */
.leaderboards div:first-child table tr {
font-family: NeonSans;
}
/* Leaderboards Div First Child Table Tr Th */
.leaderboards div:first-child table tr th {
text-align: start;
font-size: 25px;
color: #8c00ff;
text-shadow: 0 0 10px #8c00ff, 0 0 20px #8c00ff, 0 0 30px #8c00ff, 0 0 40px #8c00ff;
}
/* Leaderboards Div First Child Table Tr Td */
.leaderboards div:first-child table tr td {
font-size: 20px;
color: #f700ff;
text-shadow: 0 0 10px #f700ff, 0 0 20px #f700ff, 0 0 30px #f700ff, 0 0 40px #f700ff;
}
/* Leaderboards Div Last Child */
.leaderboards div:last-child {
width: 100%;
display: flex;
justify-content: center;
align-items: center;
}
/* Leaderboards Div Last Child A */
.leaderboards div:last-child a {
width: 160px;
padding: 30px;
padding-bottom: 40px;
font-family: NeonSans;
font-size: 20px;
color: #ff9d00;
text-shadow: 0 0 10px #ff9d00, 0 0 20px #ff9d00, 0 0 30px #ff9d00, 0 0 40px #ff9d00;
transition: text-shadow 0.2s linear;
}
/* Leaderboards Div Last Child A Hover */
.leaderboards div:last-child a:hover {
text-shadow: 0 0 10px #ff9d00, 0 0 20px #ff9d00;
transition: text-shadow 0.2s linear;
}
/* Small Screens */
@media screen and (max-width: 1111px) {
/* Leaderboards Div First Child */
.leaderboards div:first-child {
flex-direction: column-reverse;
justify-content: center;
align-items: center;
}
/* Leaderboards Div First Child Table First Child */
.leaderboards div:first-child table:first-child {
padding-top: 50px;
width: 100%;
}
/* Leaderboards Div First Child Table Last Child */
.leaderboards div:first-child table:last-child {
width: 100%;
}
}

@ -0,0 +1,4 @@
/* Play Canvas */
.play canvas {
width: 100vw;
}

@ -0,0 +1,12 @@
/* Body */
body {
background-color: #1a1a1a;
}
/* All Elements */
* {
padding: 0;
margin: 0;
border: 0;
text-decoration: none;
}
Loading…
Cancel
Save