Started networking
This commit is contained in:
parent
a834803378
commit
995be37d82
|
|
@ -0,0 +1,118 @@
|
||||||
|
# Kyler Olsen
|
||||||
|
# CS 2450 Final Project
|
||||||
|
# Apr 2025
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
import network_utilities
|
||||||
|
import socket
|
||||||
|
|
||||||
|
|
||||||
|
class Game:
|
||||||
|
|
||||||
|
__player: Player
|
||||||
|
__server: socket.socket
|
||||||
|
|
||||||
|
def __init__(self, player: Player, server: socket.socket):
|
||||||
|
self.__player = player
|
||||||
|
self.__server = server
|
||||||
|
|
||||||
|
def start_game(self):
|
||||||
|
data = network_utilities.pack_varint(1)
|
||||||
|
self.__server.send(data)
|
||||||
|
|
||||||
|
def start_round(self, difficulty: int):
|
||||||
|
data = network_utilities.pack_varint(2)
|
||||||
|
data += network_utilities.pack_varint(difficulty)
|
||||||
|
self.__server.send(data)
|
||||||
|
|
||||||
|
def guess_reference(self, url: str):
|
||||||
|
data = network_utilities.pack_varint(3)
|
||||||
|
data += network_utilities.pack_string(url)
|
||||||
|
self.__server.send(data)
|
||||||
|
|
||||||
|
def end_game(self):
|
||||||
|
data = network_utilities.pack_varint(4)
|
||||||
|
self.__server.send(data)
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
packet_id = network_utilities.unpack_varint(self.__server)
|
||||||
|
if packet_id == 1:
|
||||||
|
name = network_utilities.unpack_string(self.__server)
|
||||||
|
self.__player.player_joined(name)
|
||||||
|
elif packet_id == 2:
|
||||||
|
text = network_utilities.unpack_string(self.__server)
|
||||||
|
self.__player.new_verse(text)
|
||||||
|
elif packet_id == 3:
|
||||||
|
self.__player.guess_incorrect()
|
||||||
|
elif packet_id == 4:
|
||||||
|
self.__player.guess_correct()
|
||||||
|
elif packet_id == 5:
|
||||||
|
points = network_utilities.unpack_varint(self.__server)
|
||||||
|
url = network_utilities.unpack_string(self.__server)
|
||||||
|
player = network_utilities.unpack_string(self.__server)
|
||||||
|
self.__player.verse_guessed(points, url, player)
|
||||||
|
elif packet_id == 6:
|
||||||
|
players = network_utilities.unpack_string_array(self.__server)
|
||||||
|
scores = network_utilities.unpack_varint_array(self.__server)
|
||||||
|
self.__player.game_over(players, scores)
|
||||||
|
|
||||||
|
|
||||||
|
class Player:
|
||||||
|
|
||||||
|
__name: str
|
||||||
|
__verse: str
|
||||||
|
__score: int
|
||||||
|
__game: Game | None
|
||||||
|
|
||||||
|
def __init__(self, name: str):
|
||||||
|
self.__name = name
|
||||||
|
self.__verse = ""
|
||||||
|
self.__score = 0
|
||||||
|
self.__game = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self) -> str: return self.__name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def verse(self) -> str: return self.__verse
|
||||||
|
|
||||||
|
@property
|
||||||
|
def score(self) -> int: return self.__score
|
||||||
|
|
||||||
|
def join_game(self, host: str = 'localhost', port: int = 7788):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def start_game(self):
|
||||||
|
if self.__game is not None:
|
||||||
|
self.__game.start_game()
|
||||||
|
|
||||||
|
def guess_reference(self, url: str):
|
||||||
|
if self.__game is not None:
|
||||||
|
self.__game.guess_reference(url)
|
||||||
|
|
||||||
|
def new_round(self, difficulty: int):
|
||||||
|
if self.__game is not None:
|
||||||
|
self.__game.start_round(difficulty)
|
||||||
|
|
||||||
|
def end_game(self):
|
||||||
|
if self.__game is not None:
|
||||||
|
self.__game.end_game()
|
||||||
|
|
||||||
|
def player_joined(self, name: str):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def new_verse(self, text: str):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def guess_incorrect(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def guess_correct(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def verse_guessed(self, points: int, url: str, player: str):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def game_over(self, players: list[str], scores: list[int]):
|
||||||
|
pass
|
||||||
|
|
||||||
54
game.py
54
game.py
|
|
@ -1,54 +0,0 @@
|
||||||
# Kyler Olsen
|
|
||||||
# CS 2450 Final Project
|
|
||||||
# Apr 2025
|
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
from typing import TYPE_CHECKING
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from socket import SocketIO
|
|
||||||
|
|
||||||
class Game:
|
|
||||||
|
|
||||||
__difficulty: int
|
|
||||||
__players: list[str]
|
|
||||||
__clients: list[SocketIO]
|
|
||||||
__scores: list[int]
|
|
||||||
__active: bool
|
|
||||||
__finished: bool
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.__difficulty = 0
|
|
||||||
self.__players = []
|
|
||||||
self.__clients = []
|
|
||||||
self.__scores = []
|
|
||||||
self.__active = False
|
|
||||||
self.__finished = False
|
|
||||||
|
|
||||||
@property
|
|
||||||
def active(self) -> bool: return self.__active
|
|
||||||
|
|
||||||
@property
|
|
||||||
def finished(self) -> bool: return self.__finished
|
|
||||||
|
|
||||||
def add_player(self, name: str):
|
|
||||||
if not self.__active:
|
|
||||||
self.__players.append(name)
|
|
||||||
self.__scores.append(0)
|
|
||||||
|
|
||||||
def start_game(self):
|
|
||||||
self.__active = True
|
|
||||||
|
|
||||||
def start_round(self, difficulty: int):
|
|
||||||
if self.__active and not self.__finished:
|
|
||||||
self.__difficulty = difficulty
|
|
||||||
|
|
||||||
def new_verse(self, url: str, text: str):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def guess_reference(self, ref: str, player: str):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def end_game(self):
|
|
||||||
self.__finished = True
|
|
||||||
|
|
||||||
39
library.py
39
library.py
|
|
@ -6,21 +6,41 @@ from __future__ import annotations
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
import json
|
import json
|
||||||
import random
|
import random
|
||||||
|
import socket
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from game import Game
|
from server import Game
|
||||||
|
|
||||||
|
|
||||||
class Library:
|
class Library:
|
||||||
|
|
||||||
__verses: dict
|
__verses: dict
|
||||||
__games: list[Game]
|
__games: list[Game]
|
||||||
|
__host: str
|
||||||
|
__port: int
|
||||||
|
__socket: socket.socket
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self, host: str = '', port: int = 7788):
|
||||||
with open("data/scripture-frequencies.json", encoding='utf-8') as file:
|
with open("data/scripture-frequencies.json", encoding='utf-8') as file:
|
||||||
self.__verses = json.load(file)
|
self.__verses = json.load(file)
|
||||||
self.__games = []
|
self.__games = []
|
||||||
|
|
||||||
|
self.__host = host
|
||||||
|
self.__port = port
|
||||||
|
self.__socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
|
||||||
|
def serve_forever(self):
|
||||||
|
try:
|
||||||
|
with self.__socket as s:
|
||||||
|
s.bind((self.__host, self.__port))
|
||||||
|
s.listen(1)
|
||||||
|
while True:
|
||||||
|
conn, _ = s.accept()
|
||||||
|
...
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print("KeyboardInterrupt\nExiting...")
|
||||||
|
return
|
||||||
|
|
||||||
def join_game(self, name: str, game_num: int = -1):
|
def join_game(self, name: str, game_num: int = -1):
|
||||||
if game_num == -1:
|
if game_num == -1:
|
||||||
for i, game in enumerate(self.__games):
|
for i, game in enumerate(self.__games):
|
||||||
|
|
@ -48,11 +68,16 @@ class Library:
|
||||||
lang = 'eng'
|
lang = 'eng'
|
||||||
volume, book_url, chapter_url, verse_url = url[1:].split('/')
|
volume, book_url, chapter_url, verse_url = url[1:].split('/')
|
||||||
|
|
||||||
if volume == 'ot': filename = f"data/{lang}.old-testament.json"
|
if volume == 'ot':
|
||||||
elif volume == 'nt': filename = f"data/{lang}.new-testament.json"
|
filename = f"data/{lang}.old-testament.json"
|
||||||
elif volume == 'bofm': filename = f"data/{lang}.book-of-mormon.json"
|
elif volume == 'nt':
|
||||||
elif volume == 'dc-testament': filename = f"data/{lang}.doctrine-and-covenants.json"
|
filename = f"data/{lang}.new-testament.json"
|
||||||
elif volume == 'pgp': filename = f"data/{lang}.pearl-of-great-price.json"
|
elif volume == 'bofm':
|
||||||
|
filename = f"data/{lang}.book-of-mormon.json"
|
||||||
|
elif volume == 'dc-testament':
|
||||||
|
filename = f"data/{lang}.doctrine-and-covenants.json"
|
||||||
|
elif volume == 'pgp':
|
||||||
|
filename = f"data/{lang}.pearl-of-great-price.json"
|
||||||
|
|
||||||
with open(filename, encoding='utf-8') as file:
|
with open(filename, encoding='utf-8') as file:
|
||||||
data = json.load(file)
|
data = json.load(file)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
# Kyler Olsen
|
||||||
|
# CS 2450 Final Project
|
||||||
|
# Apr 2025
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
import struct
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from socket import socket
|
||||||
|
|
||||||
|
def pack_varint(data: int) -> bytes:
|
||||||
|
ordinal = b''
|
||||||
|
while True:
|
||||||
|
byte = data & 0x7F
|
||||||
|
data >>= 7
|
||||||
|
ordinal += struct.pack('B', byte | (0x80 if data > 0 else 0))
|
||||||
|
if data == 0:
|
||||||
|
break
|
||||||
|
return ordinal
|
||||||
|
|
||||||
|
def unpack_varint(conn: socket) -> int:
|
||||||
|
data = 0
|
||||||
|
for i in range(5):
|
||||||
|
ordinal = conn.recv(1)
|
||||||
|
if len(ordinal) == 0:
|
||||||
|
break
|
||||||
|
byte = ord(ordinal)
|
||||||
|
data |= (byte & 0x7F) << 7*i
|
||||||
|
if not byte & 0x80:
|
||||||
|
break
|
||||||
|
return data
|
||||||
|
|
||||||
|
def pack_string(text: str) -> bytes:
|
||||||
|
data = pack_varint(len(text))
|
||||||
|
data += text.encode('utf-8')
|
||||||
|
return data
|
||||||
|
|
||||||
|
def unpack_string(conn: socket) -> str:
|
||||||
|
length = unpack_varint(conn)
|
||||||
|
data = conn.recv(length)
|
||||||
|
text = data.decode('utf-8')
|
||||||
|
return text
|
||||||
|
|
||||||
|
def pack_varint_array(array: list[int]) -> bytes:
|
||||||
|
data = pack_varint(len(array))
|
||||||
|
for i in array:
|
||||||
|
data += pack_varint(i)
|
||||||
|
return data
|
||||||
|
|
||||||
|
def unpack_varint_array(conn: socket) -> list[int]:
|
||||||
|
length = unpack_varint(conn)
|
||||||
|
array: list[int] = []
|
||||||
|
for _ in range(length):
|
||||||
|
array.append(unpack_varint(conn))
|
||||||
|
return array
|
||||||
|
|
||||||
|
def pack_string_array(array: list[str]) -> bytes:
|
||||||
|
data = pack_varint(len(array))
|
||||||
|
for i in array:
|
||||||
|
data += pack_string(i)
|
||||||
|
return data
|
||||||
|
|
||||||
|
def unpack_string_array(conn: socket) -> list[str]:
|
||||||
|
length = unpack_varint(conn)
|
||||||
|
array: list[str] = []
|
||||||
|
for _ in range(length):
|
||||||
|
array.append(unpack_string(conn))
|
||||||
|
return array
|
||||||
57
player.py
57
player.py
|
|
@ -1,57 +0,0 @@
|
||||||
# Kyler Olsen
|
|
||||||
# CS 2450 Final Project
|
|
||||||
# Apr 2025
|
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
from typing import TYPE_CHECKING
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from socket import SocketIO
|
|
||||||
|
|
||||||
|
|
||||||
class Player:
|
|
||||||
|
|
||||||
__name: str
|
|
||||||
__verse: str
|
|
||||||
__score: int
|
|
||||||
__server: SocketIO | None
|
|
||||||
|
|
||||||
def __init__(self, name: str):
|
|
||||||
self.__name = name
|
|
||||||
self.__verse = ""
|
|
||||||
self.__score = 0
|
|
||||||
self.__server = None
|
|
||||||
|
|
||||||
def join_game(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def start_game(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def guess_referene(self, ref: str):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def new_round(self, difficulty: int):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def end_game(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def player_joined(self, name: str):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def new_verse(self, text: str):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def guess_incorrect(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def guess_correct(self, points: int):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def verse_guessed(self, url: str, player: str):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def game_over(self, players: list[str], scores: list[int]):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,130 @@
|
||||||
|
# Kyler Olsen
|
||||||
|
# CS 2450 Final Project
|
||||||
|
# Apr 2025
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
import network_utilities
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from socket import socket
|
||||||
|
from library import Library
|
||||||
|
|
||||||
|
|
||||||
|
class Game:
|
||||||
|
|
||||||
|
__library: Library
|
||||||
|
__current_url: str
|
||||||
|
__clients: list[Player]
|
||||||
|
__round_points: list[int]
|
||||||
|
__total_scores: list[int]
|
||||||
|
__active: bool
|
||||||
|
__finished: bool
|
||||||
|
|
||||||
|
def __init__(self, library: Library):
|
||||||
|
self.__library = library
|
||||||
|
self.__current_url = ""
|
||||||
|
self.__clients = []
|
||||||
|
self.__round_points = []
|
||||||
|
self.__total_scores = []
|
||||||
|
self.__active = False
|
||||||
|
self.__finished = False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def active(self) -> bool: return self.__active
|
||||||
|
|
||||||
|
@property
|
||||||
|
def finished(self) -> bool: return self.__finished
|
||||||
|
|
||||||
|
def add_player(self, name: str, socket: socket):
|
||||||
|
if not self.__active:
|
||||||
|
self.__clients.append(Player(name, self, socket))
|
||||||
|
self.__total_scores.append(0)
|
||||||
|
for player in self.__clients:
|
||||||
|
player.player_joined(name)
|
||||||
|
|
||||||
|
def start_game(self):
|
||||||
|
self.__active = True
|
||||||
|
|
||||||
|
def start_round(self, difficulty: int):
|
||||||
|
if self.__active and not self.__finished:
|
||||||
|
self.__round_points = [0] * len(self.__clients)
|
||||||
|
self.__library.get_verse(difficulty, self)
|
||||||
|
|
||||||
|
def new_verse(self, url: str, text: str):
|
||||||
|
for player in self.__clients:
|
||||||
|
self.__current_url = url
|
||||||
|
player.new_verse(text)
|
||||||
|
|
||||||
|
def guess_reference(self, url: str, player: Player):
|
||||||
|
if self.__active and not self.__finished:
|
||||||
|
if url == self.__current_url:
|
||||||
|
player.guess_correct()
|
||||||
|
for i, points in enumerate(self.__round_points):
|
||||||
|
self.__total_scores[i] += points
|
||||||
|
self.__clients[i].verse_guessed(
|
||||||
|
points, self.__current_url, player.name)
|
||||||
|
else:
|
||||||
|
player.guess_incorrect()
|
||||||
|
|
||||||
|
def end_game(self):
|
||||||
|
self.__finished = True
|
||||||
|
|
||||||
|
|
||||||
|
class Player:
|
||||||
|
|
||||||
|
__name: str
|
||||||
|
__game: Game
|
||||||
|
__client: socket
|
||||||
|
|
||||||
|
def __init__(self, name: str, game: Game, socket: socket):
|
||||||
|
self.__name = name
|
||||||
|
self.__game = game
|
||||||
|
self.__client = socket
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self) -> str: return self.__name
|
||||||
|
|
||||||
|
def player_joined(self, name: str):
|
||||||
|
data = network_utilities.pack_varint(1)
|
||||||
|
data += network_utilities.pack_string(name)
|
||||||
|
self.__client.send(data)
|
||||||
|
|
||||||
|
def new_verse(self, text: str):
|
||||||
|
data = network_utilities.pack_varint(2)
|
||||||
|
data += network_utilities.pack_string(text)
|
||||||
|
self.__client.send(data)
|
||||||
|
|
||||||
|
def guess_incorrect(self):
|
||||||
|
data = network_utilities.pack_varint(3)
|
||||||
|
self.__client.send(data)
|
||||||
|
|
||||||
|
def guess_correct(self):
|
||||||
|
data = network_utilities.pack_varint(4)
|
||||||
|
self.__client.send(data)
|
||||||
|
|
||||||
|
def verse_guessed(self, points: int, url: str, player: str):
|
||||||
|
data = network_utilities.pack_varint(5)
|
||||||
|
data += network_utilities.pack_varint(points)
|
||||||
|
data += network_utilities.pack_string(url)
|
||||||
|
data += network_utilities.pack_string(player)
|
||||||
|
self.__client.send(data)
|
||||||
|
|
||||||
|
def game_over(self, players: list[str], scores: list[int]):
|
||||||
|
data = network_utilities.pack_varint(6)
|
||||||
|
data += network_utilities.pack_string_array(players)
|
||||||
|
data += network_utilities.pack_varint_array(scores)
|
||||||
|
self.__client.send(data)
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
packet_id = network_utilities.unpack_varint(self.__client)
|
||||||
|
if packet_id == 1:
|
||||||
|
self.__game.start_game()
|
||||||
|
elif packet_id == 2:
|
||||||
|
difficulty = network_utilities.unpack_varint(self.__client)
|
||||||
|
self.__game.start_round(difficulty)
|
||||||
|
elif packet_id == 3:
|
||||||
|
url = network_utilities.unpack_string(self.__client)
|
||||||
|
self.__game.guess_reference(url, self)
|
||||||
|
elif packet_id == 4:
|
||||||
|
self.__game.end_game()
|
||||||
Loading…
Reference in New Issue