Compare commits
No commits in common. "master" and "console" have entirely different histories.
9
.gitignore
vendored
9
.gitignore
vendored
|
@ -1,2 +1,7 @@
|
|||
server/node_modules/*
|
||||
gitUpdate.sh
|
||||
client/bin/*
|
||||
client/obj/*
|
||||
client/.idea/*
|
||||
|
||||
server/bin/*
|
||||
server/obj/*
|
||||
server/.idea/*
|
592
client/Client.cs
Normal file
592
client/Client.cs
Normal file
|
@ -0,0 +1,592 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Net.Sockets;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using System;
|
||||
using SocketLib;
|
||||
|
||||
namespace SnakeGame {
|
||||
class SnakeGame {
|
||||
private static int width = 32; // Laptop: 85 ; RPI-LED: 32
|
||||
private static int height = 32; // Laptop: 38 ; RPI-LED: 32
|
||||
private static char[,] prev, board;
|
||||
private static string lobbyCode = "";
|
||||
private static int FPS = 10, frameCount = 0;
|
||||
private static Segment food = new();
|
||||
private static List<Snake> snakes = new();
|
||||
private static bool gameOver, isReady, isMultiplayer;
|
||||
private static Snake self = new(width/2, height/2);
|
||||
|
||||
private static void Main(string[] args) {
|
||||
// Check CL_Args for parameters
|
||||
bool doSingleplayer = true;
|
||||
foreach (string arg in args)
|
||||
if (arg == "-s" || arg == "--single") {
|
||||
doSingleplayer = false;
|
||||
break;
|
||||
}
|
||||
|
||||
// Calculate the number of milliseconds in which to display 1 frame
|
||||
int frameMills = 1000 / FPS;
|
||||
|
||||
// Initialize both types of boards
|
||||
Console.ForegroundColor = ConsoleColor.White;
|
||||
prev = new char[width, height];
|
||||
initBoard(prev);
|
||||
board = new char[width, height];
|
||||
initBoard(board);
|
||||
|
||||
// Decide on Single- or Multiplayer version
|
||||
Console.Clear();
|
||||
while (doSingleplayer) {
|
||||
Console.Clear();
|
||||
Console.WriteLine("################################");
|
||||
Console.WriteLine("Art des Spielens:");
|
||||
Console.WriteLine("0) Schließen");
|
||||
Console.WriteLine("1) Singleplayer");
|
||||
Console.WriteLine("2) Multiplayer");
|
||||
Console.WriteLine("################################");
|
||||
Console.WriteLine();
|
||||
Console.Write("Wahl (0-2): ");
|
||||
string input = Console.ReadLine();
|
||||
|
||||
// Handle user input with switch statement
|
||||
bool ready = true;
|
||||
switch (input) {
|
||||
default:
|
||||
// Invalid input was given
|
||||
Console.Write("Ungültige Eingabe oder Fehler. Bitte erneut versuchen. ");
|
||||
Console.ReadKey(true);
|
||||
ready = false;
|
||||
break;
|
||||
case "0":
|
||||
// Exit program
|
||||
Environment.Exit(0);
|
||||
break;
|
||||
case "1":
|
||||
// Play as Singleplayer: No more operatiosn required
|
||||
break;
|
||||
case "2":
|
||||
// Redirect to multiplayer options, reset if multiplayer wasn't successfully started
|
||||
ready = multiplayerConnect();
|
||||
break;
|
||||
}
|
||||
// Only break out of while-Loop on successful selection
|
||||
if (ready)
|
||||
break;
|
||||
}
|
||||
|
||||
// Initial Board Draw
|
||||
Console.Clear();
|
||||
Console.WriteLine();
|
||||
initialDrawBoard();
|
||||
|
||||
// Console Output Setup
|
||||
if (isMultiplayer) {
|
||||
Console.SetCursorPosition(width/2, 0);
|
||||
Console.Write($"Lobby Code: {lobbyCode}");
|
||||
Connection.Send(Connection.self, "PlayerReady");
|
||||
isReady = true;
|
||||
}
|
||||
|
||||
// Game Play
|
||||
Stopwatch stopwatch = new();
|
||||
while (!gameOver) {
|
||||
// Start stopwatch
|
||||
stopwatch.Restart();
|
||||
// Handle key presses and exiting
|
||||
self.keyHandler();
|
||||
// Handle snake movement
|
||||
self.move();
|
||||
// Handle snake dying
|
||||
self.touchHandler(self);
|
||||
if (isMultiplayer)
|
||||
for (int i = 0; i < snakes.Count; i++)
|
||||
self.touchHandler(snakes[i]);
|
||||
// Handle snake eating food
|
||||
self.foodHandler(food);
|
||||
|
||||
// Add food position
|
||||
board[food.x, food.y] = 'O';
|
||||
|
||||
// Update Board and Display Snakes
|
||||
updateSnakePos();
|
||||
drawChangesOfBoard();
|
||||
|
||||
// Display Debug / Score
|
||||
Console.ForegroundColor = ConsoleColor.White;
|
||||
Console.SetCursorPosition(0, 0);
|
||||
Console.WriteLine($"Score: {self.length-self.baseLen}");
|
||||
|
||||
// Save previous snakes
|
||||
if (isMultiplayer) {
|
||||
snakes = new();
|
||||
if (isReady)
|
||||
Connection.Send(Connection.self, $"PlayerUpdate {self.ToString()}");
|
||||
}
|
||||
|
||||
// Save board as previous and init new
|
||||
prev = board;
|
||||
board = new char[width, height];
|
||||
initBoard(board);
|
||||
frameCount = (frameCount+1)%1000000;
|
||||
|
||||
// Fix framerate
|
||||
long elapsed = stopwatch.ElapsedMilliseconds;
|
||||
if (elapsed < frameMills)
|
||||
Thread.Sleep(frameMills - (int)elapsed);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool multiplayerConnect() {
|
||||
// Initialize connection parameters
|
||||
Connection.Initialize();
|
||||
Connection.messageEvents.Add(new("PlayerUpdate", playerUpdate));
|
||||
Connection.messageEvents.Add(new("FoodUpdate", foodUpdate));
|
||||
Connection.messageEvents.Add(new("Created", onGetCode));
|
||||
Connection.messageEvents.Add(new("Joined", onGetFood));
|
||||
Connection.messageEvents.Add(new("ShowLobbies", showLobbies));
|
||||
|
||||
// Ask for user input for IP address and port number
|
||||
Console.WriteLine("Angabe der IP Adresse und Portnummer des anderen Servers");
|
||||
Console.Write("( Format 'IPv4:Port' ): ");
|
||||
string ipIn = Console.ReadLine();
|
||||
if (ipIn.ToLower() == "exit")
|
||||
return false;
|
||||
Connection.ClientConnect(ipIn);
|
||||
|
||||
// Handle lobby-connect
|
||||
while (true) {
|
||||
Console.Clear();
|
||||
Console.WriteLine("################################");
|
||||
Console.WriteLine("Beitreten einer Lobby:");
|
||||
Console.WriteLine("0) Zurück");
|
||||
Console.WriteLine("1) Lobby erstellen");
|
||||
Console.WriteLine("2) Lobby beitreten");
|
||||
Console.WriteLine("################################");
|
||||
Console.WriteLine();
|
||||
Console.Write("Wahl (0-2): ");
|
||||
string input = Console.ReadLine();
|
||||
|
||||
// Handle user input with switch statement
|
||||
bool ready = true;
|
||||
switch (input) {
|
||||
default:
|
||||
// Invalid input was given
|
||||
Console.Write("Ungültige Eingabe oder Fehler. Bitte erneut versuchen. ");
|
||||
Console.ReadKey(true);
|
||||
ready = false;
|
||||
break;
|
||||
case "0":
|
||||
// Return to previous section
|
||||
return false;
|
||||
case "1":
|
||||
// Open new Lobby on connected Server
|
||||
string msg = $"NewLobby {food.ToString()}";
|
||||
Connection.Send(Connection.self, msg);
|
||||
break;
|
||||
case "2":
|
||||
// List opened lobbies and such
|
||||
ready = multiplayerLobbyList();
|
||||
break;
|
||||
}
|
||||
// Only break out of while-Loop on successful selection
|
||||
if (ready)
|
||||
break;
|
||||
}
|
||||
// Wait for server-side response on lobby creation/join
|
||||
while (lobbyCode == "")
|
||||
Thread.Sleep(500);
|
||||
// Exit section in multiplayer mode
|
||||
isMultiplayer = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool multiplayerLobbyList() {
|
||||
// Ask server for open lobbies
|
||||
Connection.Send(Connection.self, "ListLobbies");
|
||||
// Wait until lobby list is received
|
||||
while (lobbyCode == "")
|
||||
Thread.Sleep(500);
|
||||
// Interpret the lobby list
|
||||
string[] listed = lobbyCode.Split('-');
|
||||
lobbyCode = "";
|
||||
|
||||
// Start section for lobby selection
|
||||
int current = 0;
|
||||
while (true) {
|
||||
Console.Clear();
|
||||
Console.WriteLine("################################");
|
||||
Console.WriteLine("Beitreten einer Lobby:");
|
||||
Console.WriteLine("0) Zurück");
|
||||
// Loop through available lobbies from #<current> and up to 5 lobbies ahead
|
||||
for (int i = 1; i < 5; i++)
|
||||
if (current+i < listed.Length+1) {
|
||||
string[] next = listed[current+i-1].Split(',');
|
||||
Console.WriteLine($"{i}) Code: {next[0]} - Spieler: {next[1]}");
|
||||
}
|
||||
// Only display option when there are more available
|
||||
if (listed.Length > current+5)
|
||||
Console.WriteLine("6) Weiter");
|
||||
Console.WriteLine("################################");
|
||||
Console.WriteLine();
|
||||
Console.Write("Wahl (0-2): ");
|
||||
string input = Console.ReadLine();
|
||||
|
||||
// Handle user input with switch statement
|
||||
bool ready = true;
|
||||
switch (input) {
|
||||
default:
|
||||
// Check if input corresponds to a lobby, else an invalid input was given
|
||||
int parse = int.TryParse(input, out parse) ? parse : 0;
|
||||
if (parse <= 0) {
|
||||
// Output invalid input message
|
||||
Console.Write("Ungültige Eingabe oder Fehler. Bitte erneut versuchen. ");
|
||||
Console.ReadKey(true);
|
||||
ready = false;
|
||||
// Select lobby from user input
|
||||
} else
|
||||
lobbyCode = listed[parse-1].Split(',')[0];
|
||||
break;
|
||||
case "0":
|
||||
// If not at the beginning of the list, go back in the list
|
||||
if (current != 0) {
|
||||
ready = false;
|
||||
current -= 5;
|
||||
break;
|
||||
}
|
||||
// Otherwise exit section
|
||||
return false;
|
||||
case "6":
|
||||
// Go forward in the list if not already at the end
|
||||
ready = false;
|
||||
if (listed.Length > current+5)
|
||||
current += 5;
|
||||
else {
|
||||
// Output invalid input message
|
||||
Console.Write("Ungültige Eingabe oder Fehler. Bitte erneut versuchen. ");
|
||||
Console.ReadKey(true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
// Only break out of while-Loop on successful selection
|
||||
if (ready)
|
||||
break;
|
||||
}
|
||||
// Join the selected lobby, continue in multiplayer mode
|
||||
Connection.Send(Connection.self, $"JoinLobby {lobbyCode}");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Async Callback to receive the list of open lobbies
|
||||
private static void showLobbies(Socket other, string rest) {
|
||||
lobbyCode = rest;
|
||||
}
|
||||
|
||||
// Async Callback to receive an update from another player
|
||||
private static void playerUpdate(Socket other, string rest) {
|
||||
Snake parsedSnake = Snake.Parse(rest);
|
||||
if (parsedSnake != null)
|
||||
snakes.Add(parsedSnake);
|
||||
}
|
||||
|
||||
// Async Callback to receive an update for food
|
||||
private static void foodUpdate(Socket other, string rest) {
|
||||
try {
|
||||
string[] splits = rest.Split(',');
|
||||
food = new(int.Parse(splits[0]), int.Parse(splits[1]));
|
||||
board[food.x, food.y] = 'O';
|
||||
} catch (Exception e) { }
|
||||
}
|
||||
|
||||
// Async Callback to receive the lobby code from the server
|
||||
private static void onGetCode(Socket other, string code) {
|
||||
lobbyCode = code;
|
||||
}
|
||||
|
||||
// Async Callback to receive the lobby code and current food position in lobby
|
||||
private static void onGetFood(Socket other, string rest) {
|
||||
string[] splits = rest.Split('-');
|
||||
lobbyCode = splits[0];
|
||||
food = Segment.Parse(splits[1]);
|
||||
}
|
||||
|
||||
// Method to check for changes between boards and update screen at these positions
|
||||
private static void drawChangesOfBoard() {
|
||||
for (int j = 0; j < board.GetLength(1); j++)
|
||||
for (int i = 0; i < board.GetLength(0); i++)
|
||||
if (board[i, j] != prev[i, j]) {
|
||||
if (self.IsPartOfSelf(i, j))
|
||||
Console.ForegroundColor = ConsoleColor.Green;
|
||||
else {
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
for (int k = 0; k < snakes.Count; k++)
|
||||
if (snakes[k].IsPartOfSelf(i, j))
|
||||
Console.ForegroundColor = ConsoleColor.Blue;
|
||||
}
|
||||
Console.SetCursorPosition(i*2+1, j+2);
|
||||
Console.Write(board[i, j]);
|
||||
}
|
||||
}
|
||||
|
||||
// Method to initialize a board Object
|
||||
private static void initBoard(char[,] _board) {
|
||||
for (int i = 0; i < _board.GetLength(0); i++)
|
||||
for (int j = 0; j < _board.GetLength(1); j++)
|
||||
_board[i, j] = ' ';
|
||||
}
|
||||
|
||||
// Update all snakes' positions onto the current board
|
||||
private static void updateSnakePos() {
|
||||
List<Snake> current = copySnakes(snakes);
|
||||
current.Add(self.Copy());
|
||||
for (int i = 0; i < current.Count; i++) {
|
||||
board[current[i].ownPos.x, current[i].ownPos.y] = '$';
|
||||
foreach (Segment _segment in current[i].tailPos)
|
||||
board[_segment.x, _segment.y] = 'S';
|
||||
}
|
||||
}
|
||||
|
||||
// Method to initially draw the game board onto the screen
|
||||
private static void initialDrawBoard() {
|
||||
for (int j = 0; j < board.GetLength(1)+2; j++) {
|
||||
if (j % (board.GetLength(1)+1) == 0) {
|
||||
Console.WriteLine("".PadRight(width*2+1, '#'));
|
||||
} else {
|
||||
Console.Write("#");
|
||||
for (int i = 0; i < board.GetLength(0); i++) {
|
||||
string conditional = (i < board.GetLength(0) - 1) ? "|" : "";
|
||||
Console.Write(board[i, j-1] + conditional);
|
||||
}
|
||||
Console.WriteLine("#");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Method to reset any given snake to its base parameters
|
||||
private static void resetSelf(Snake other) {
|
||||
// Replace any characters at score
|
||||
int scoreLen = $"Score: {other.length-other.baseLen}".Length;
|
||||
Console.SetCursorPosition(0, 0);
|
||||
for (int i = 0; i < scoreLen; i++)
|
||||
Console.Write(' ');
|
||||
|
||||
// Reset lengt of Snake
|
||||
other.length = other.baseLen;
|
||||
}
|
||||
|
||||
// Method to copy any given list of snakes into a non-memory-reference list of non-memory-reference snakes
|
||||
private static List<Snake> copySnakes(List<Snake> _snakes) {
|
||||
List<Snake> newSnakes = new();
|
||||
for (int i = 0; i < _snakes.Count; i++)
|
||||
newSnakes.Add(_snakes[i].Copy());
|
||||
return newSnakes;
|
||||
}
|
||||
|
||||
// Method to parse a serialized list of 'Segments' into a List<Segment> Object
|
||||
private static List<Segment> parseSegments(string _segments) {
|
||||
List<Segment> newSegments = new();
|
||||
string[] splits = _segments.Split('-');
|
||||
foreach (string s in splits)
|
||||
newSegments.Add(Segment.Parse(s));
|
||||
return newSegments;
|
||||
}
|
||||
|
||||
// Class to represent an instance of a Snake
|
||||
public class Snake {
|
||||
public Segment ownPos, lastPos;
|
||||
public List<Segment> tailPos = new();
|
||||
public int length = 0, baseLen = 0;
|
||||
public int mx = 1, my = 0;
|
||||
|
||||
// Constructor for a simple Snake with x and y coordinates
|
||||
public Snake(int _x, int _y, int _len=5) {
|
||||
baseLen = _len;
|
||||
length = _len;
|
||||
ownPos = new(_x, _y);
|
||||
// Prepare Tail based on length, position and base movement
|
||||
tailPos.Add(new(_x-mx, _y));
|
||||
for (int i = 0; i < _len-1; i++) {
|
||||
Segment previous = tailPos[0];
|
||||
Segment next = new(previous.x-mx, previous.y);
|
||||
tailPos.Insert(0, next);
|
||||
}
|
||||
// Prepare lastPos as non-null
|
||||
Segment lastNew = tailPos[0];
|
||||
lastPos = new(lastNew.x-mx, lastNew.y);
|
||||
// Set random heading
|
||||
switch ((new Random()).Next(3)) {
|
||||
case 0:
|
||||
mx = 1; my = 0;
|
||||
break;
|
||||
case 1:
|
||||
mx = 0; my = 1;
|
||||
break;
|
||||
case 2:
|
||||
mx = 0; my = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Constructor to copy a snake from given parameters (to avoid object reference from memory)
|
||||
public Snake(Segment _ownPos, Segment _lastPos, List<Segment> _tailPos, int _mx, int _my, int _length, int _baseLen) {
|
||||
ownPos = _ownPos.Copy();
|
||||
lastPos = _lastPos.Copy();
|
||||
foreach (Segment t in _tailPos)
|
||||
tailPos.Add(t.Copy());
|
||||
mx = _mx;
|
||||
my = _my;
|
||||
length = _length;
|
||||
baseLen = _baseLen;
|
||||
}
|
||||
|
||||
// Method to interpret a string as a serialized snake
|
||||
public static Snake Parse(string snake) {
|
||||
Snake parsedSnake = null;
|
||||
// Scratch process if any inconsistencies in the format cause trouble
|
||||
try {
|
||||
string[] attributes = snake.Split(';');
|
||||
parsedSnake = new(
|
||||
Segment.Parse(attributes[0]),
|
||||
Segment.Parse(attributes[1]),
|
||||
parseSegments(attributes[2]),
|
||||
int.Parse(attributes[3]),
|
||||
int.Parse(attributes[4]),
|
||||
int.Parse(attributes[5]),
|
||||
int.Parse(attributes[6])
|
||||
);
|
||||
}
|
||||
catch (Exception) { }
|
||||
|
||||
return parsedSnake;
|
||||
}
|
||||
|
||||
// Serialize this Snake Object into a string
|
||||
public override string ToString() {
|
||||
string segStr = "";
|
||||
for (int i = 0; i < tailPos.Count; i++)
|
||||
segStr += tailPos[i].ToString() + ((i < tailPos.Count-1) ? "-" : "");
|
||||
return $"{ownPos.ToString()};{lastPos.ToString()};{segStr};{mx};{my};{length};{baseLen}";
|
||||
}
|
||||
|
||||
// Check if any given x and y coordinates are part of this Snake Object (including Tail/Segments)
|
||||
public bool IsPartOfSelf(int _x, int _y) {
|
||||
foreach (Segment s in tailPos)
|
||||
if (s.x == _x && s.y == _y)
|
||||
return true;
|
||||
return ownPos.x == _x && ownPos.y == _y;
|
||||
}
|
||||
|
||||
// Method to handle collision of this snake with any other given snake
|
||||
public void touchHandler(Snake other) {
|
||||
// If is not self, check for head collision
|
||||
if (!other.Equals(this)) {
|
||||
bool xC = ownPos.x == other.ownPos.x;
|
||||
bool yC = ownPos.y == other.ownPos.y;
|
||||
if (xC && yC)
|
||||
resetSelf(this);
|
||||
}
|
||||
|
||||
// Check for tail collision
|
||||
foreach (Segment pos in other.tailPos)
|
||||
if (pos.x == ownPos.x && pos.y == ownPos.y) {
|
||||
resetSelf(this);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Method to handle collision with a Food (Segment) Object, 'consume' it if possible
|
||||
public void foodHandler(Segment _food) {
|
||||
// Check for eating food
|
||||
if (ownPos.x == _food.x && ownPos.y == _food.y) {
|
||||
length++;
|
||||
food.setRandom(width, height);
|
||||
if (isMultiplayer && this == self)
|
||||
Connection.Send(Connection.self, $"FoodUpdate {food.ToString()}");
|
||||
}
|
||||
}
|
||||
|
||||
// Method to handle a keypress for this snake
|
||||
public void keyHandler() {
|
||||
// If key is pressed, handle logic
|
||||
if (Console.KeyAvailable)
|
||||
switch (Console.ReadKey(true).Key) {
|
||||
case ConsoleKey.Escape:
|
||||
Environment.Exit(0);
|
||||
break;
|
||||
case ConsoleKey.UpArrow:
|
||||
if (my != 1) { my = -1; mx = 0; }
|
||||
break;
|
||||
case ConsoleKey.DownArrow:
|
||||
if (my != -1) { my = 1; mx = 0; }
|
||||
break;
|
||||
case ConsoleKey.LeftArrow:
|
||||
if (mx != 1) { mx = -1; my = 0; }
|
||||
break;
|
||||
case ConsoleKey.RightArrow:
|
||||
if (mx != -1) { mx = 1; my = 0; }
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Method to calculate movement of this Snake
|
||||
public void move() {
|
||||
// Move snake according to last input and add prev. pos. to tail
|
||||
tailPos.Add(ownPos.Copy());
|
||||
ownPos.x = (ownPos.x + mx + width) % width;
|
||||
ownPos.y = (ownPos.y + my + height) % height;
|
||||
|
||||
// Remove any tail above current length
|
||||
while (tailPos.Count > length) {
|
||||
lastPos = tailPos[0];
|
||||
tailPos.RemoveAt(0);
|
||||
}
|
||||
}
|
||||
|
||||
public Snake Copy() {
|
||||
return new(ownPos, lastPos, tailPos, mx, my, length, baseLen);
|
||||
}
|
||||
}
|
||||
|
||||
// Base class for logic and positions, i.e. a Segment of a Snake or Food
|
||||
public class Segment {
|
||||
public int x, y;
|
||||
|
||||
// Constructor to save given x and y coordinates
|
||||
public Segment(int _x, int _y) {
|
||||
x = _x;
|
||||
y = _y;
|
||||
}
|
||||
|
||||
// Constructor to set own x and y to random coordinates constrained by global width and height
|
||||
public Segment() {
|
||||
setRandom(width, height);
|
||||
}
|
||||
|
||||
// Try parsing any given string into a Segment object. Return 'new Segment(0, 0)' as default.
|
||||
public static Segment Parse(string segment) {
|
||||
string[] splits = segment.Split(',');
|
||||
int _x = int.TryParse(splits[0], out _x) ? _x : 0;
|
||||
int _y = int.TryParse(splits[1], out _y) ? _y : 0;
|
||||
return new(_x, _y);
|
||||
}
|
||||
|
||||
// Serialize Segment object into a string
|
||||
public override string ToString() {
|
||||
return $"{x},{y}";
|
||||
}
|
||||
|
||||
// Set own x and y coordinates randomly according to give width and height boundaries
|
||||
public void setRandom(int _width, int _height) {
|
||||
Random rng = new();
|
||||
x = rng.Next(_width);
|
||||
y = rng.Next(_height);
|
||||
}
|
||||
|
||||
// Create copy of this instance of a Segment Object
|
||||
public Segment Copy() {
|
||||
return new(x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
19
client/Client.csproj
Normal file
19
client/Client.csproj
Normal file
|
@ -0,0 +1,19 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<!-- Reference to local copy of https://gitlab1.ptb.de/waltem01/csharpsocketserver -->
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\SocketTest\Net.Sockets\SocketLib.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<RuntimeIdentifier>win10-x64</RuntimeIdentifier>
|
||||
<SelfContained>true</SelfContained>
|
||||
<PublishSingleFile>true</PublishSingleFile>
|
||||
<PublishReadyToRun>true</PublishReadyToRun>
|
||||
<IncludeAllContentForSelfExtract>true</IncludeAllContentForSelfExtract>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
|
@ -1,9 +0,0 @@
|
|||
<html>
|
||||
<head>
|
||||
</head>
|
||||
<body onload="loaded = true">
|
||||
<script src="/socket.io/socket.io.js"></script>
|
||||
<script src="js/client.js"></script>
|
||||
<script src="js/canvas.js"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,216 +0,0 @@
|
|||
var canv = document.createElement("canvas");
|
||||
const ww = window.innerWidth;
|
||||
const wh = window.innerHeight;
|
||||
const rc = ww > wh;
|
||||
const cw = (rc) ? ww : wh;
|
||||
const ch = (rc) ? wh : ww;
|
||||
canv.width = ww;
|
||||
canv.height = wh;
|
||||
canv.style.position = "absolute";
|
||||
canv.style.top = "0px";
|
||||
canv.style.left = "0px";
|
||||
var ctx = canv.getContext("2d");
|
||||
document.body.appendChild(canv);
|
||||
var mouse = {x: 0, y: 0};
|
||||
document.addEventListener("mousemove", evt => {
|
||||
var rect = canv.getBoundingClientRect();
|
||||
mouse = {
|
||||
x: evt.clientX - rect.left,
|
||||
y: evt.clientY - rect.top
|
||||
};
|
||||
});
|
||||
document.addEventListener("mousedown", () => {
|
||||
const mx = (rc) ? mouse.x : mouse.y;
|
||||
const my = (rc) ? mouse.y : mouse.x;
|
||||
const LEFT = mx < cw * (1/3);
|
||||
const RIGHT = mx > cw * (2/3);
|
||||
const BOTTOM = my > ch * (2/3);
|
||||
const TOP = my < ch * (1/3);
|
||||
const copy = self.b;
|
||||
if (LEFT && !BOTTOM && !TOP) {
|
||||
//Left
|
||||
addKeyCode(37);
|
||||
} else if (RIGHT && !BOTTOM && !TOP) {
|
||||
//Right
|
||||
addKeyCode(39);
|
||||
} else if (BOTTOM && !RIGHT && !LEFT) {
|
||||
//Down
|
||||
addKeyCode(38);
|
||||
} else if (TOP && !RIGHT && !LEFT) {
|
||||
//Up
|
||||
addKeyCode(40);
|
||||
}
|
||||
});
|
||||
document.addEventListener("keydown",evt => {
|
||||
addKeyCode(evt.keyCode);
|
||||
});
|
||||
function addKeyCode(kC) {
|
||||
let isInB = false, isOpposite = false;
|
||||
for (let i = 0; i < self.b.length; i++) {
|
||||
const e = self.b[i];
|
||||
if (e == kC) {
|
||||
isInB = true;
|
||||
break;
|
||||
}
|
||||
/*
|
||||
Didn't get this to work, lol.
|
||||
Instead just checked for opposite on line 54, 60, 66, 72.
|
||||
|
||||
if (e == kC-2 || e == kC+2) {
|
||||
isOpposite = true;
|
||||
break;
|
||||
}*/
|
||||
}
|
||||
if (!isOpposite && !isInB) {
|
||||
self.b.push(kC);
|
||||
}
|
||||
}
|
||||
class Snake {
|
||||
constructor(px, py) {
|
||||
this.x = px;
|
||||
this.y = py;
|
||||
this.h = {x: 0, y: 0};
|
||||
this.l = 5;
|
||||
this.t = Array();
|
||||
this.b = [Math.floor(Math.random()*4+37)];
|
||||
this.p = {x: 0, y: 0};
|
||||
}
|
||||
|
||||
// Didn't want each socket.emit() to also sent update and show as method from Snake, so seperate functions it is.
|
||||
}
|
||||
|
||||
const cells = 40, cwidth = ch/cells, fCL = 6;
|
||||
let self = new Snake(Math.floor(cells/2), Math.floor(cells/2)), fC = 0;
|
||||
for (let i = 0; i < self.l; i++) {
|
||||
update(self, true);
|
||||
}
|
||||
|
||||
function update(snake, ignore = false) {
|
||||
if (fC%fCL == 0) {
|
||||
const e = snake.b.splice(0,1)[0];
|
||||
switch (e) {
|
||||
case 37:
|
||||
if (snake.h.x != 1) {
|
||||
snake.h.x = -1;
|
||||
snake.h.y = 0;
|
||||
}
|
||||
break;
|
||||
case 38:
|
||||
if (snake.h.y != 1) {
|
||||
snake.h.x = 0;
|
||||
snake.h.y = -1;
|
||||
}
|
||||
break;
|
||||
case 39:
|
||||
if (snake.h.x != -1) {
|
||||
snake.h.x = 1;
|
||||
snake.h.y = 0;
|
||||
}
|
||||
break;
|
||||
case 40:
|
||||
if (snake.h.y != -1) {
|
||||
snake.h.x = 0;
|
||||
snake.h.y = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
snake.t.push({x: snake.x, y: snake.y});
|
||||
while (snake.t.length > snake.l) {
|
||||
snake.p = snake.t.splice(0,1);
|
||||
//snake.t.shift();
|
||||
}
|
||||
|
||||
snake.x = (snake.x + snake.h.x + cells) % cells;
|
||||
snake.y = (snake.y + snake.h.y + cells) % cells;
|
||||
|
||||
if (snake.x == food.x && snake.y == food.y) {
|
||||
snake.l++;
|
||||
socket.emit('snakeFoodUpdate');
|
||||
}
|
||||
|
||||
for (let i = 0; i < snake.t.length; i++) {
|
||||
if (snake.t[i].x == snake.x && snake.t[i].y == snake.y) {
|
||||
snake.l = 5;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < players.length; i++) {
|
||||
if (players[i].x == snake.x && players[i].y == snake.y) {
|
||||
snake.l = 5;
|
||||
break;
|
||||
}
|
||||
let broke = false;
|
||||
for (let j = 0; j < players[i].t.length; j++) {
|
||||
if (players[i].t[j].x == snake.x && players[i].t[j].y == snake.y) {
|
||||
snake.l = 5;
|
||||
broke = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (broke) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ignore) {
|
||||
socket.emit('snakePlayerUpdate', {ownID: selfID, data: snake});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function show(snake) {
|
||||
if (snake == self) {
|
||||
ctx.fillStyle = "green";
|
||||
ctx.strokeStyle = "green";
|
||||
} else {
|
||||
ctx.fillStyle = "blue";
|
||||
ctx.strokeStyle = "blue";
|
||||
}
|
||||
const sx = (rc) ? snake.x : snake.y;
|
||||
const sy = (rc) ? snake.y : snake.x;
|
||||
ctx.fillRect(sx*cwidth+1, sy*cwidth+1, cwidth-2, cwidth-2);
|
||||
|
||||
if (snake == self) {
|
||||
ctx.fillStyle = "lime";
|
||||
ctx.strokeStyle = "lime";
|
||||
} else {
|
||||
ctx.fillStyle = "cyan";
|
||||
ctx.strokeStyle = "cyan";
|
||||
}
|
||||
snake.t.forEach(e => {
|
||||
const ex = (rc) ? e.x : e.y;
|
||||
const ey = (rc) ? e.y : e.x;
|
||||
ctx.fillRect(ex*cwidth+1, ey*cwidth+1, cwidth-2, cwidth-2);
|
||||
});
|
||||
|
||||
ctx.fillStyle = "red";
|
||||
ctx.strokeStyle = "red";
|
||||
const fx = (rc) ? food.x : food.y;
|
||||
const fy = (rc) ? food.y : food.x;
|
||||
ctx.fillRect(fx*cwidth+1, fy*cwidth+1, cwidth-2, cwidth-2);
|
||||
}
|
||||
|
||||
var frames = 60, interv = setInterval(() => {
|
||||
ctx.fillStyle = "grey";
|
||||
ctx.fillRect(0,0,canv.width,canv.height);
|
||||
if (selfID != -1 && loaded) {
|
||||
var t = ctx.getTransform();
|
||||
const nwidth = cw/2-ch/2;
|
||||
if (rc) {
|
||||
ctx.translate(nwidth, 0);
|
||||
} else {
|
||||
ctx.translate(0, nwidth);
|
||||
}
|
||||
ctx.fillStyle = "black";
|
||||
ctx.fillRect(0, 0, ch, ch);
|
||||
|
||||
update(self);
|
||||
show(self);
|
||||
players.forEach(show);
|
||||
|
||||
ctx.setTransform(t);
|
||||
fC = (fC+1001)%1000;
|
||||
}
|
||||
}, 1000/frames);
|
|
@ -1,18 +0,0 @@
|
|||
const socket = io();
|
||||
let selfID = -1, players = Array(), loaded = false, food = {x: -1, y: -1};
|
||||
|
||||
socket.on('connected', d => {
|
||||
//console.log("Connected!");
|
||||
players = d.pdata;
|
||||
selfID = d.ownID;
|
||||
food = d.food;
|
||||
});
|
||||
|
||||
socket.on('snakeUpdatePlayers', p => {
|
||||
//console.log("Recieved "+ p.pdata.length +" players.");
|
||||
players = Array();
|
||||
p.pdata.forEach(e => {
|
||||
players.push(e);
|
||||
});
|
||||
food = p.food;
|
||||
});
|
193
server/Server.cs
Normal file
193
server/Server.cs
Normal file
|
@ -0,0 +1,193 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Net.Sockets;
|
||||
using SocketLib;
|
||||
using System;
|
||||
|
||||
|
||||
// Initialize global variables and Server
|
||||
List<Lobby> lobbies = new List<Lobby>();
|
||||
Connection.Initialize();
|
||||
Connection.disconnectMethod = removeFromLobby;
|
||||
Connection.connectMethod = connectOutput;
|
||||
Connection.messageMethod = rawMessageDebug;
|
||||
|
||||
// Create onEvent methods
|
||||
Connection.messageEvents.Add(new MessageEvent("NewLobby", onNewLobby));
|
||||
Connection.messageEvents.Add(new MessageEvent("JoinLobby", onJoinLobby));
|
||||
// Redirect other messages
|
||||
Connection.messageEvents.Add(new MessageEvent("PlayerUpdate", playerUpdateRedirect));
|
||||
Connection.messageEvents.Add(new MessageEvent("PlayerReady", playerDetectReady));
|
||||
Connection.messageEvents.Add(new MessageEvent("FoodUpdate", foodUpdateRedirect));
|
||||
Connection.messageEvents.Add(new MessageEvent("ListLobbies", playerListLobbies));
|
||||
|
||||
// Check CL_Args for parameters
|
||||
bool defaultIP = true;
|
||||
string[] CL_Args = Environment.GetCommandLineArgs();
|
||||
foreach (string arg in CL_Args)
|
||||
if (arg == "-y" || arg == "--yes") {
|
||||
defaultIP = false;
|
||||
break;
|
||||
}
|
||||
|
||||
// Start the Server itself
|
||||
if (defaultIP) {
|
||||
Console.Write($"Use '{Connection.ipAddress}:{Connection.port}'? (Y/n) ");
|
||||
string uIn = Console.ReadLine();
|
||||
if (uIn.ToLower() == "n") {
|
||||
Console.WriteLine("Input IP and Port in the following Format:");
|
||||
Console.Write("( Format 'IPv4:Port' ): ");
|
||||
uIn = Console.ReadLine();
|
||||
Connection.ParseIPandPort(uIn);
|
||||
}
|
||||
}
|
||||
|
||||
// Output selected IP and Port
|
||||
Console.Clear();
|
||||
Console.WriteLine("Server Started . . . ");
|
||||
Console.WriteLine($"Using '{Connection.ipAddress}:{Connection.port}'.");
|
||||
Connection.ServerStart();
|
||||
|
||||
void connectOutput(Socket other) {
|
||||
Console.WriteLine("New client has connected ...");
|
||||
}
|
||||
|
||||
void rawMessageDebug(Socket other, string rest) {
|
||||
string info = "";
|
||||
foreach (Lobby l in lobbies)
|
||||
if (l.users.Contains(other)) {
|
||||
info = $"{l.users.IndexOf(other)} {l.code}";
|
||||
break;
|
||||
}
|
||||
string[] splits = (info == "") ? new string[0] : info.Split(' ');
|
||||
string lobbyMsg = (splits.Length == 0) ? "" : $"#{splits[0]} from Lobby '{splits[1]}' ";
|
||||
|
||||
Console.WriteLine($"User {lobbyMsg}has sent Message '{rest}'.");
|
||||
}
|
||||
|
||||
void playerListLobbies(Socket other) {
|
||||
string lobbyCodes = "";
|
||||
for (int i = 0; i < lobbies.Count; i++) {
|
||||
Lobby l = lobbies[i];
|
||||
string sepCon = (i < lobbies.Count-1) ? "-" : "";
|
||||
lobbyCodes += $"{l.code},{l.users.Count}{sepCon}";
|
||||
}
|
||||
Connection.Send(other, $"ShowLobbies {lobbyCodes}");
|
||||
}
|
||||
|
||||
void playerDetectReady(Socket other) {
|
||||
foreach (Lobby l in lobbies)
|
||||
if (l.users.Contains(other)) {
|
||||
l.ready[l.users.IndexOf(other)] = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void foodUpdateRedirect(Socket other, string rest) {
|
||||
foreach (Lobby l in lobbies)
|
||||
if (l.users.Contains(other)) {
|
||||
l.food = rest;
|
||||
for (int i = 0; i < l.users.Count; i++)
|
||||
if (!l.users[i].Equals(other) && l.ready[i])
|
||||
Connection.Send(l.users[i], $"FoodUpdate {rest}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void playerUpdateRedirect(Socket other, string rest) {
|
||||
redirect(other, $"PlayerUpdate {rest}");
|
||||
}
|
||||
|
||||
void redirect(Socket other, string message) {
|
||||
foreach (Lobby l in lobbies)
|
||||
if (l.users.Contains(other)) {
|
||||
for (int i = 0; i < l.users.Count; i++)
|
||||
if (!l.users[i].Equals(other) && l.ready[i])
|
||||
Connection.Send(l.users[i], message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void onNewLobby(Socket other, string _food) {
|
||||
// Create a new Lobby
|
||||
Random rng = new Random();
|
||||
string lobbyCode = "";
|
||||
while (lobbyCode.Length < 6) {
|
||||
if (rng.Next(2) == 0)
|
||||
lobbyCode += (char)rng.Next(48, 58);
|
||||
else
|
||||
lobbyCode += (char)rng.Next(65, 91);
|
||||
}
|
||||
Lobby temp = new Lobby(other, lobbyCode);
|
||||
temp.food = _food;
|
||||
lobbies.Add(temp);
|
||||
Connection.Send(other, $"Created {lobbyCode}");
|
||||
}
|
||||
|
||||
void onJoinLobby(Socket other, string lobbyCode) {
|
||||
// Add user to lobby or else give feedback
|
||||
string response = "";
|
||||
Lobby? temp = null;
|
||||
foreach (Lobby l in lobbies) {
|
||||
if (l.users.Contains(other)) {
|
||||
response = $"Already in a Lobby";
|
||||
break;
|
||||
} else if (l.code == lobbyCode) {
|
||||
l.users.Add(other);
|
||||
l.ready.Add(false);
|
||||
response = $"Joined {lobbyCode}-{l.food}";
|
||||
temp = l;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Handle feedback for Lobbies
|
||||
if (response == "")
|
||||
response = "No such lobby code.";
|
||||
Connection.Send(other, response);
|
||||
|
||||
// if (temp != null)
|
||||
// if (temp.users.Count >= temp.count) {
|
||||
// for (int i = 0; i < temp.users.Count; i++)
|
||||
// Connection.Send(temp.users[i], $"FullLobby");
|
||||
// Console.WriteLine($"Started lobby with code: '{temp.code}'.");
|
||||
// }
|
||||
}
|
||||
|
||||
void removeFromLobby(Socket self) {
|
||||
// Remove client from lobby, and lobby from list
|
||||
int lobbyPos = -1;
|
||||
for (int i = lobbies.Count - 1; i >= 0; i--) {
|
||||
Lobby l = lobbies[i];
|
||||
if (l.users.Contains(self)) {
|
||||
int index = l.users.IndexOf(self);
|
||||
lobbyPos = index;
|
||||
l.users.RemoveAt(index);
|
||||
l.ready.RemoveAt(index);
|
||||
foreach (Socket user in l.users)
|
||||
Connection.Send(user, $"UserLeft");
|
||||
if (l.users.Count == 0) {
|
||||
Console.WriteLine($"Closed lobby with code: '{l.code}'.");
|
||||
lobbies.Remove(l);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
Console.WriteLine($"Client {((lobbyPos >= 0) ? $"#{lobbyPos} " : "")}has disconnected ...");
|
||||
}
|
||||
|
||||
// Custom Lobby class
|
||||
class Lobby {
|
||||
// Keep track of own lobby code and list of users
|
||||
public List<Socket> users;
|
||||
public List<bool> ready;
|
||||
public string code, food;
|
||||
|
||||
// Initialize Lobby with current user in list and given lobby code
|
||||
public Lobby(Socket other, string lobbyCode) {
|
||||
users = new List<Socket>();
|
||||
ready = new List<bool>();
|
||||
users.Add(other);
|
||||
ready.Add(false);
|
||||
code = lobbyCode;
|
||||
food = "";
|
||||
}
|
||||
}
|
19
server/Server.csproj
Normal file
19
server/Server.csproj
Normal file
|
@ -0,0 +1,19 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<!-- Reference to local copy of https://gitlab1.ptb.de/waltem01/csharpsocketserver -->
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\SocketTest\Net.Sockets\SocketLib.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<RuntimeIdentifier>win10-x64</RuntimeIdentifier>
|
||||
<SelfContained>true</SelfContained>
|
||||
<PublishSingleFile>true</PublishSingleFile>
|
||||
<PublishReadyToRun>true</PublishReadyToRun>
|
||||
<IncludeAllContentForSelfExtract>true</IncludeAllContentForSelfExtract>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
4019
server/package-lock.json
generated
4019
server/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
|
@ -1,22 +0,0 @@
|
|||
{
|
||||
"name": "server",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"dev": "nodemon server.js",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"axios": "^0.24.0",
|
||||
"cheerio": "^1.0.0-rc.10",
|
||||
"express": "^4.17.3",
|
||||
"global-agent": "^3.0.0",
|
||||
"nodemon": "^2.0.15",
|
||||
"reddit-image-fetcher": "^2.0.10",
|
||||
"reddit.images": "^1.0.7",
|
||||
"socket.io": "^4.4.1"
|
||||
}
|
||||
}
|
|
@ -1,98 +0,0 @@
|
|||
const port = 3000;
|
||||
|
||||
const http = require('http');
|
||||
const express = require('express');
|
||||
const socketio = require('socket.io');
|
||||
const { exec } = require('child_process');
|
||||
|
||||
const app = express();
|
||||
const clientPath = __dirname+'/../client';
|
||||
console.log('Serving static from ' + clientPath);
|
||||
app.use(express.static(clientPath));
|
||||
|
||||
const server = http.createServer(app);
|
||||
const io = socketio(server);
|
||||
|
||||
app.post('/receiveUpdate', (req, res) => {
|
||||
res.status(200).json({success:true});
|
||||
exec(`sh ${__dirname}/../gitUpdate.sh`, (error, stdout, stderr) => {
|
||||
console.log(stdout);
|
||||
console.error(stderr);
|
||||
if (error !== null) {
|
||||
console.error(`exec error: ${error}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
server.on('error', err => {
|
||||
console.error('Server error:', err);
|
||||
});
|
||||
|
||||
server.listen(port, () => {
|
||||
console.log('Server Started on Port '+port);
|
||||
});
|
||||
|
||||
const cells = 40;
|
||||
let snakePlayers = Array(), snakeFood = {x: Math.floor(Math.random()*cells), y: Math.floor(Math.random()*cells)};
|
||||
|
||||
io.on('connection', socket => {
|
||||
let id = -1;
|
||||
|
||||
console.log("New Player has connected.");
|
||||
while (true) {
|
||||
let current = Math.floor(Math.random()*89+10);
|
||||
let isUsed = false;
|
||||
for (let i = 0; i < snakePlayers.length; i++) {
|
||||
if (snakePlayers[i].uid == current) {
|
||||
isUsed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!isUsed) {
|
||||
id = current;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (id != -1) {
|
||||
console.log('Handing ID', id);
|
||||
let temp = Array();
|
||||
snakePlayers.forEach(p => {
|
||||
if (p.data != null) {
|
||||
temp.push(p.data);
|
||||
}
|
||||
});
|
||||
socket.emit('connected', {pdata: temp, ownID: id, food: snakeFood});
|
||||
snakePlayers.push({socket, ownID: id, data: null});
|
||||
}
|
||||
|
||||
socket.on('disconnect', () => {
|
||||
for (let i = 0; i < snakePlayers.length; i++) {
|
||||
if (snakePlayers[i].socket == socket) {
|
||||
console.log("Disconnecting SNAKEGAME User. ID", snakePlayers[i].ownID);
|
||||
snakePlayers.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('snakeFoodUpdate', () => {
|
||||
snakeFood = {x: Math.floor(Math.random()*cells), y: Math.floor(Math.random()*cells)};
|
||||
});
|
||||
|
||||
socket.on('snakePlayerUpdate', p => {
|
||||
for (let i = 0; i < snakePlayers.length; i++) {
|
||||
if (p.ownID == snakePlayers[i].ownID) {
|
||||
snakePlayers[i].data = p.data;
|
||||
let temp = Array();
|
||||
snakePlayers.forEach(d => {
|
||||
if (d.ownID != p.ownID && d.data != null) {
|
||||
temp.push(d.data);
|
||||
}
|
||||
});
|
||||
snakePlayers[i].socket.emit('snakeUpdatePlayers', {pdata: temp, food: snakeFood});
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue
Block a user