lobby add works need to do ready up next

This commit is contained in:
2023-04-23 18:58:10 -07:00
parent 4ab107eee2
commit 41f9433de5
6 changed files with 321 additions and 184 deletions

4
.idea/workspace.xml generated
View File

@@ -5,8 +5,12 @@
</component>
<component name="ChangeListManager">
<list default="true" id="67d0f8e4-ef35-4641-9e95-eb79cf01a045" name="Changes" comment="">
<change afterPath="$PROJECT_DIR$/constants.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/client.py" beforeDir="false" afterPath="$PROJECT_DIR$/client.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/common.py" beforeDir="false" afterPath="$PROJECT_DIR$/common.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/server.py" beforeDir="false" afterPath="$PROJECT_DIR$/server.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/vckonline.py" beforeDir="false" afterPath="$PROJECT_DIR$/vckonline.py" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />

147
client.py
View File

@@ -1,49 +1,128 @@
import wx
import socket
import threading
from constants import *
import json
class ClientVCKO(wx.App):
def __init__(self):
super().__init__()
self.frame = None
self.connection_status = None
self.in_lobby = None
self.player_id = None
def OnInit(self):
self.frame = MyFrame(self)
self.frame.Show()
self.connection_status = False
self.in_lobby = False
self.player_id = False
self.frame.set_connection_status()
return True
def parse_response(self, response):
first_word = response.split()[0]
match first_word:
case "lobby":
full_command = response.split()
if full_command[1] == "joined" and len(full_command) == 3:
self.player_id = full_command[2]
self.in_lobby = True
print(self.player_id)
else:
print("Couldn't understand that response")
case _:
print(response)
class MyFrame(wx.Frame):
def __init__(self):
super().__init__(parent=None, title='VCK Online')
panel = wx.Panel(self)
my_sizer = wx.BoxSizer(wx.VERTICAL)
self.text_ctrl = wx.TextCtrl(panel)
my_sizer.Add(self.text_ctrl, 0, wx.ALL | wx.EXPAND, 5)
my_btn = wx.Button(panel, label='Press Me')
my_btn.Bind(wx.EVT_BUTTON, self.on_press)
my_sizer.Add(my_btn, 0, wx.ALL | wx.CENTER, 5)
panel.SetSizer(my_sizer)
self.host = "lukesau.com"
self.port = 8328 # socket server port number
self.header_size = 1024
self.format = "utf-8"
self.disconnect_message = "!DISCONNECT"
def __init__(self, app):
super().__init__(parent=None, title='VCK Online', size=Constants.default_window_size)
self.app = app
self.panel = wx.Panel(self)
self.vertical_sizer = wx.BoxSizer(wx.VERTICAL)
self.status_sizer = wx.BoxSizer(wx.HORIZONTAL)
self.message_field = wx.TextCtrl(self.panel, style=wx.TE_PROCESS_ENTER)
self.connection_status_indicator = wx.StaticText(self.panel, label="Connection Status")
self.my_btn = wx.Button(self.panel, label="Send call")
self.my_btn.Bind(wx.EVT_BUTTON, self.on_press)
self.message_field.Bind(wx.EVT_TEXT_ENTER, self.on_text_enter)
# Create a horizontal sizer to hold the connection_status StaticText
self.status_sizer.AddStretchSpacer()
self.status_sizer.Add(wx.StaticText(self.panel), 0, wx.EXPAND | wx.RIGHT, 5)
self.status_sizer.Add(self.connection_status_indicator, 0, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT, 5)
self.status_sizer.Add(wx.StaticText(self.panel), 0, wx.EXPAND | wx.LEFT, 5)
# Add the text field, button, and status sizer to the vertical sizer
self.vertical_sizer.Add(self.message_field, 0, wx.ALL | wx.EXPAND, 5)
self.vertical_sizer.Add(self.my_btn, 0, wx.ALL | wx.CENTER, 5)
self.vertical_sizer.AddStretchSpacer()
self.vertical_sizer.Add(self.status_sizer, 0, wx.ALIGN_LEFT | wx.BOTTOM, 5)
self.panel.SetSizer(self.vertical_sizer)
self.SetMinSize(Constants.minimum_window_size)
self.Show()
# Create a timer to call the connection_check method every 2 seconds
self.timer = wx.Timer(self)
self.Bind(wx.EVT_TIMER, self.set_connection_status, self.timer)
self.timer.Start(5000)
def set_connection_status(self, event=None):
if connection_check():
self.connection_status_indicator.SetLabel("Connected")
self.connection_status_indicator.SetForegroundColour(Constants.green)
else:
self.connection_status_indicator.SetLabel("Not Connected")
self.connection_status_indicator.SetForegroundColour(Constants.red)
def on_press(self, event):
message = self.text_ctrl.GetValue()
message = self.message_field.GetValue()
if not message:
print("You didn't enter anything!")
else:
client_socket = socket.socket()
client_socket.connect((self.host, self.port))
self.send(message, client_socket)
self.text_ctrl.SetValue("")
self.send(self.disconnect_message, client_socket)
self.api_call(message)
def send(self, msg, input_socket):
message = msg.encode(self.format)
msg_length = len(message)
print(msg_length)
send_length = str(msg_length).encode(self.format)
send_length += b' ' * (self.header_size - len(send_length))
input_socket.send(send_length)
input_socket.send(message)
print("done sending")
print(input_socket.recv(2048).decode(self.format))
def on_text_enter(self, event):
message = self.message_field.GetValue()
if not message:
print("You didn't enter anything!")
else:
self.api_call(message)
def api_call(self, message):
if connection_check():
self.app.parse_response(send(message))
self.message_field.SetValue("")
def connection_check():
try:
response = send("connection_check")
if response == "received":
return True
except ConnectionRefusedError:
return False
except BrokenPipeError:
return False
def _send(msg, input_socket):
message = msg.encode(Constants.text_format)
msg_length = len(message)
send_length = str(msg_length).encode(Constants.text_format)
send_length += b' ' * (Constants.header_size - len(send_length))
input_socket.send(send_length)
input_socket.send(message)
return input_socket.recv(2048).decode(Constants.text_format)
def send(message):
client_socket = socket.socket()
client_socket.connect((Constants.host, Constants.port))
response = _send(message, client_socket)
return response
if __name__ == '__main__':
app = wx.App()
frame = MyFrame()
app.MainLoop()
the_app = ClientVCKO()
the_app.MainLoop()

286
common.py
View File

@@ -1,6 +1,8 @@
import mysql.connector
import random
from typing import List
import shortuuid
import uuid
class Card:
@@ -17,8 +19,9 @@ class Card:
class Player:
def __init__(self):
self.name = "Player"
def __init__(self, name, player_id):
self.player_id = player_id
self.name = name
self.owned_starters = []
self.owned_citizens = []
self.owned_domains = []
@@ -173,8 +176,9 @@ class Duke(Card):
self.expansion = expansion
class Board:
class Game:
def __init__(self, player_count, preset="shuffled", number_of_dukes=2):
self.game_id = uuid.uuid4()
self.player_count = player_count
self.preset = preset
self.number_of_dukes = number_of_dukes
@@ -254,10 +258,144 @@ class Board:
self.monster_stack.append(my_monster)
my_connect.close()
# end load game data
self.remove_extra_cards()
# remove extra cards
if self.player_count != 5:
extra_monsters = []
remaining_monsters = []
for monster in self.monster_stack:
if monster.is_extra == 1:
extra_monsters.append(monster)
else:
remaining_monsters.append(monster)
self.monster_stack = remaining_monsters
self.graveyard.extend(extra_monsters)
match self.preset:
case "base1":
base1_monsters = []
other_expansion_monsters = []
for monster in self.monster_stack:
if monster.expansion == "base1":
base1_monsters.append(monster)
else:
other_expansion_monsters.append(monster)
self.monster_stack = base1_monsters
self.graveyard.extend(other_expansion_monsters)
base1_citizens = []
other_expansion_citizens = []
for citizen in self.citizen_stack:
if citizen.expansion == "base1":
base1_citizens.append(citizen)
else:
other_expansion_citizens.append(citizen)
self.citizen_stack = base1_citizens
self.graveyard.extend(other_expansion_citizens)
case "base2":
base1_monsters = []
base2_monsters = []
other_expansion_monsters = []
for monster in self.monster_stack:
if monster.expansion == "base1":
base1_monsters.append(monster)
elif monster.expansion == "base2":
base2_monsters.append(monster)
else:
other_expansion_monsters.append(monster)
# add 2 random monster areas from base1 to fill out base2 monsters
grouped_monsters = {}
for base1_monster in base1_monsters:
area = base1_monster.area
if area in grouped_monsters:
grouped_monsters[area].append(base1_monster)
else:
grouped_monsters[area] = [base1_monster]
areas = list(grouped_monsters.keys())
chosen_areas = random.sample(areas, 2)
not_chosen_monsters = [monster for area, monsters in grouped_monsters.items() if
area not in chosen_areas for monster in monsters]
self.graveyard.extend(not_chosen_monsters)
for i, area in enumerate(chosen_areas):
monsters = grouped_monsters[area]
base2_monsters.extend(monsters)
self.monster_stack = base2_monsters
self.graveyard.extend(other_expansion_monsters)
base2_citizens = []
other_expansion_citizens = []
for citizen in self.citizen_stack:
if citizen.expansion == "base2":
base2_citizens.append(citizen)
else:
other_expansion_citizens.append(citizen)
# add peasant and knight from base1
for citizen in other_expansion_citizens:
if citizen.name == "Peasant" and citizen.expansion == "base1":
base2_citizens.append(citizen)
elif citizen.name == "Knight" and citizen.expansion == "base1":
base2_citizens.append(citizen)
self.citizen_stack = base2_citizens
# put the rest of the cards in the graveyard
grouped_citizens = {}
for citizen in other_expansion_citizens:
expansion = citizen.expansion
if expansion in grouped_citizens:
grouped_citizens[expansion].append(citizen)
else:
grouped_citizens[expansion] = [citizen]
if "base1" in grouped_citizens:
base1_citizens = grouped_citizens["base1"]
base1_citizens = [citizen for citizen in base1_citizens if
citizen.name not in ("Peasant", "Knight")]
grouped_citizens["base1"] = base1_citizens
other_expansion_citizens = []
for expansion in grouped_citizens.values():
other_expansion_citizens.extend(expansion)
self.graveyard.extend(other_expansion_citizens)
case "shadowvale":
shadowvale_monsters = []
other_expansion_monsters = []
for monster in self.monster_stack:
if monster.expansion == "shadowvale":
shadowvale_monsters.append(monster)
else:
other_expansion_monsters.append(monster)
self.monster_stack = shadowvale_monsters
self.graveyard.extend(other_expansion_monsters)
shadowvale_citizens = []
other_expansion_citizens = []
for citizen in self.citizen_stack:
if citizen.expansion == "shadowvale":
shadowvale_citizens.append(citizen)
else:
other_expansion_citizens.append(citizen)
self.citizen_stack = shadowvale_citizens
self.graveyard.extend(other_expansion_citizens)
case "flamesandfrost":
flamesandfrost_monsters = []
other_expansion_monsters = []
for monster in self.monster_stack:
if monster.expansion == "flamesandfrost":
flamesandfrost_monsters.append(monster)
else:
other_expansion_monsters.append(monster)
self.monster_stack = flamesandfrost_monsters
self.graveyard.extend(other_expansion_monsters)
flamesandfrost_citizens = []
other_expansion_citizens = []
for citizen in self.citizen_stack:
if citizen.expansion == "flamesandfrost":
flamesandfrost_citizens.append(citizen)
else:
other_expansion_citizens.append(citizen)
self.citizen_stack = flamesandfrost_citizens
self.graveyard.extend(other_expansion_citizens)
case _:
if self.player_count != 5:
for stack in self.monster_grid:
# Remove monsters with isExtra = True from each stack
stack[:] = [monster for monster in stack if not monster.is_extra]
# end remove extra cards
# create players and determine order
for x in range(0, self.player_count):
my_player = Player()
my_player = Player(shortuuid.uuid())
my_player.name = f"Player {(x + 1)}"
self.player_list.append(my_player)
random.shuffle(self.player_list)
@@ -317,140 +455,9 @@ class Board:
else: # other domains are not visible or accessible
domain = self.domain_stack.pop()
stack.append(domain)
self.get_board_state()
self.get_game_state()
def remove_extra_cards(self):
if self.player_count != 5:
extra_monsters = []
remaining_monsters = []
for monster in self.monster_stack:
if monster.is_extra == 1:
extra_monsters.append(monster)
else:
remaining_monsters.append(monster)
self.monster_stack = remaining_monsters
self.graveyard.extend(extra_monsters)
match self.preset:
case "base1":
base1_monsters = []
other_expansion_monsters = []
for monster in self.monster_stack:
if monster.expansion == "base1":
base1_monsters.append(monster)
else:
other_expansion_monsters.append(monster)
self.monster_stack = base1_monsters
self.graveyard.extend(other_expansion_monsters)
base1_citizens = []
other_expansion_citizens = []
for citizen in self.citizen_stack:
if citizen.expansion == "base1":
base1_citizens.append(citizen)
else:
other_expansion_citizens.append(citizen)
self.citizen_stack = base1_citizens
self.graveyard.extend(other_expansion_citizens)
case "base2":
base1_monsters = []
base2_monsters = []
other_expansion_monsters = []
for monster in self.monster_stack:
if monster.expansion == "base1":
base1_monsters.append(monster)
elif monster.expansion == "base2":
base2_monsters.append(monster)
else:
other_expansion_monsters.append(monster)
# add 2 random monster areas from base1 to fill out base2 monsters
grouped_monsters = {}
for base1_monster in base1_monsters:
area = base1_monster.area
if area in grouped_monsters:
grouped_monsters[area].append(base1_monster)
else:
grouped_monsters[area] = [base1_monster]
areas = list(grouped_monsters.keys())
chosen_areas = random.sample(areas, 2)
not_chosen_monsters = [monster for area, monsters in grouped_monsters.items() if area not in chosen_areas for monster in monsters]
self.graveyard.extend(not_chosen_monsters)
for i, area in enumerate(chosen_areas):
monsters = grouped_monsters[area]
base2_monsters.extend(monsters)
self.monster_stack = base2_monsters
self.graveyard.extend(other_expansion_monsters)
base2_citizens = []
other_expansion_citizens = []
for citizen in self.citizen_stack:
if citizen.expansion == "base2":
base2_citizens.append(citizen)
else:
other_expansion_citizens.append(citizen)
for citizen in other_expansion_citizens:
if citizen.name == "Peasant" and citizen.expansion == "base1":
base2_citizens.append(citizen)
elif citizen.name == "Knight" and citizen.expansion == "base1":
base2_citizens.append(citizen)
self.citizen_stack = base2_citizens
grouped_citizens = {}
for citizen in other_expansion_citizens:
expansion = citizen.expansion
if expansion in grouped_citizens:
grouped_citizens[expansion].append(citizen)
else:
grouped_citizens[expansion] = [citizen]
if "base1" in grouped_citizens:
base1_citizens = grouped_citizens["base1"]
base1_citizens = [citizen for citizen in base1_citizens if citizen.name not in ("Peasant", "Knight")]
grouped_citizens["base1"] = base1_citizens
other_expansion_citizens = []
for expansion in grouped_citizens.values():
other_expansion_citizens.extend(expansion)
self.graveyard.extend(other_expansion_citizens)
case "shadowvale":
shadowvale_monsters = []
other_expansion_monsters = []
for monster in self.monster_stack:
if monster.expansion == "shadowvale":
shadowvale_monsters.append(monster)
else:
other_expansion_monsters.append(monster)
self.monster_stack = shadowvale_monsters
self.graveyard.extend(other_expansion_monsters)
shadowvale_citizens = []
other_expansion_citizens = []
for citizen in self.citizen_stack:
if citizen.expansion == "shadowvale":
shadowvale_citizens.append(citizen)
else:
other_expansion_citizens.append(citizen)
self.citizen_stack = shadowvale_citizens
self.graveyard.extend(other_expansion_citizens)
case "flamesandfrost":
flamesandfrost_monsters = []
other_expansion_monsters = []
for monster in self.monster_stack:
if monster.expansion == "flamesandfrost":
flamesandfrost_monsters.append(monster)
else:
other_expansion_monsters.append(monster)
self.monster_stack = flamesandfrost_monsters
self.graveyard.extend(other_expansion_monsters)
flamesandfrost_citizens = []
other_expansion_citizens = []
for citizen in self.citizen_stack:
if citizen.expansion == "flamesandfrost":
flamesandfrost_citizens.append(citizen)
else:
other_expansion_citizens.append(citizen)
self.citizen_stack = flamesandfrost_citizens
self.graveyard.extend(other_expansion_citizens)
case _:
if self.player_count != 5:
for stack in self.monster_grid:
# Remove monsters with isExtra = True from each stack
stack[:] = [monster for monster in stack if not monster.is_extra]
def get_board_state(self):
def get_game_state(self):
for i, monster_list in enumerate(self.monster_grid):
print(
f"Monster Stack {i + 1}: {[f'{monster.name} ({monster.monster_id})' + ('E' if monster.is_extra else '') + ('V' if monster.is_visible else '') + ('A' if monster.is_accessible else '') for monster in monster_list]}")
@@ -460,6 +467,9 @@ class Board:
for i, domain_list in enumerate(self.domain_grid):
print(
f"Domain Stack {i + 1}: {[f'{domain.name} ({domain.domain_id})' + ('V' if domain.is_visible else '') + ('A' if domain.is_accessible else '') for domain in domain_list]}")
for i, player in enumerate(self.player_list):
print(
f"Player {i + 1}: {[f'{player.name} ({player.player_id})' + (' *' if player.is_first else '') + f' G{player.gold_score} S{player.strength_score} M{player.magic_score}']}")
print(f"monster stack size {len(self.monster_stack)}")
print(f"citizen stack size {len(self.citizen_stack)}")
print(f"domain stack size {len(self.domain_stack)}")

9
constants.py Normal file
View File

@@ -0,0 +1,9 @@
class Constants:
green = (106, 171, 115)
red = (219, 92, 92)
host = "127.0.1.1"
port = 8328
header_size = 1024
text_format = "utf-8"
minimum_window_size = (300, 150)
default_window_size = (300, 150)

View File

@@ -2,31 +2,59 @@ import socket
import time
import threading
from common import *
from constants import *
import json
class ServerVCKO:
def __init__(self):
self.host = socket.gethostname()
self.port = 8328
self.header_size = 1024
self.format = "utf-8"
self.disconnect_message = "!DISCONNECT"
self.server_socket = socket.socket()
self.server_socket.bind((self.host, self.port))
self.server_socket.bind((self.host, Constants.port))
self.game_list = []
self.lobby = []
def handle_client(self, conn, addr):
print(f"Connection from: {addr}")
connected = True
while connected:
msg_length = conn.recv(self.header_size).decode(self.format)
msg_length = conn.recv(Constants.header_size).decode(Constants.text_format)
if msg_length:
msg_length = int(msg_length)
msg = conn.recv(msg_length).decode(self.format)
if msg == self.disconnect_message:
connected = False
msg = conn.recv(msg_length).decode(Constants.text_format)
first_word = msg.split()[0]
match first_word:
case "connection_check":
connected = False
conn.send("received".encode(Constants.text_format))
case "lobby":
connected = False
full_command = msg.split()
if full_command[1] == "join" and len(full_command) > 2:
joining_player_name = ' '.join(full_command[2:])
joining_player_id = shortuuid.uuid()
joining_player = LobbyMember(joining_player_name, joining_player_id)
self.lobby.append(joining_player)
message = f"lobby joined {joining_player_id}"
conn.send(joining_player_id.encode(Constants.text_format))
elif full_command[1] == "get_status":
lobby_data = []
for lobby_member in self.lobby:
player_dict = {
"name": lobby_member.name,
"player_id": lobby_member.player_id,
"is_ready": lobby_member.is_ready
}
lobby_data.append(player_dict)
json_data = json.dumps(lobby_data)
print(json_data)
conn.send(json_data.encode(Constants.text_format))
else:
conn.send("invalid message".encode(Constants.text_format))
case _:
connected = False
conn.send("invalid message".encode(Constants.text_format))
print(f"[{addr}] {msg}")
conn.send("msg received".encode(self.format))
conn.close()
def start(self):
@@ -39,6 +67,13 @@ class ServerVCKO:
print(f"Active threads: {threading.active_count() - 1}")
class LobbyMember:
def __init__(self, player_name, player_id):
self.name = player_name
self.player_id = player_id
self.is_ready = False
if __name__ == '__main__':
print("server starting")
server = ServerVCKO()

View File

@@ -2,6 +2,6 @@ from common import *
print("Welcome to Valeria Card Kingdoms: Online")
player_count = 4
citizen_set = "shadowvale" # base1, base2, shadowvale, flamesandfrost, crimsonseas, shuffled
game_board = Board(player_count, citizen_set)
citizen_set = "base2" # base1, base2, shadowvale, flamesandfrost, crimsonseas, shuffled
game_board = Game(player_count, citizen_set)
game_board.play_turn()