Reputation: 630
So as part of my project (2D Multiplayer Card Game), I've figured out how to host and run a server script online. My plan is to have two separate kivy clients connect to the server (which will just be a script with commands).
However I'm somewhat confused about the order of operations because I think the client connection is potentially in conflict with the message loop so I'm wondering if someone could basically tell me what I should be doing:
I'm going to be using this as my serverscript:
import socket
serversocket = socket.socket()
host = 'INSERTIPHERE'
port = PORTHERE
serversocket.bind(('', port))
serversocket.listen(1)
while True:
clientsocket,addr = serversocket.accept()
print("got a connection from %s" % str(addr))
msg = 'Thank you for connecting' + "\r\n"
clientsocket.send(msg.encode('ascii'))
clientsocket.close()
This is my client connection function
def Main():
host = 'INSERTIPHERE'
port = PORTHERE
mySocket = socket.socket()
mySocket.connect((host, port))
message = input(' -> ')
while message != 'q':
mySocket.send(message.encode())
data = mySocket.recv(1024).decode()
print('Received from server: ' + data)
message = input(' -> ')
mySocket.close()
Note: I understand that the server and client aren't perfectly aligned in functions but provided I can at least a connection confirmation for now, I can work from there.
I'm basically wondering how do I put this code into a simple kivy app like this:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
class BoxWidget(BoxLayout):
pass
class BoxApp(App):
def build(self):
return BoxWidget()
if __name__ == '__main__':
BoxApp().run()
My best guess is that you want to:
I also understand that Kivy has built in solutions with Twisted but I'm having trouble with the python 2-3 differences.
Thank you for reading.
Just to clarify: All I want to do right now is open a blank window and also have a confirmation message sent to the command line (or failing that a label in the window).
Upvotes: 5
Views: 12726
Reputation: 630
I got a basic version of it working with buttons. Both on local machine and online. This Solution is likely not viable for many real time apps or even a chat server since the reply has to be initiated. However for my goal of a multiplayer card game it should more than suffice with proper conditionals.
video of test on local machine
EDIT: In the video I talk about double clicking. I have just realized this is because the first click is putting the window back in focus.
EDIT 2: Using TextInput in kv file instead of input in Python file.
server script:
import socket
def Main():
host = '127.0.0.1'
port = 7000
mySocket = socket.socket()
mySocket.bind((host,port))
mySocket.listen(1)
conn, addr = mySocket.accept()
print ("Connection from: " + str(addr))
message = 'Thank you connecting'
conn.send(message.encode())
while True:
data = conn.recv(1024).decode()
strdata = str(data)
print(strdata)
reply = 'confirmed'
conn.send(reply.encode())
mySocket.close()
if __name__ == '__main__':
Main()
This is a pretty simple server. Listen for a single client, confirm connection, open a send and receive message loop.
This is the client script which isn't hugely complicated really:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import ObjectProperty
import socket
class BoxWidget(BoxLayout):
s = socket.socket()
host = '127.0.0.1'
port = 7000
display = ObjectProperty()
def connect_to_server(self):
# called by a Button press
# Connects to the server
self.s.connect((self.host, self.port))
# Receives confirmation from Server
data = self.s.recv(1024).decode()
# Converts confirmation to string
strdata = str(data)
# Prints confirmation
print(strdata)
def send_message(self):
# Is called by the function below
# Encodes and sends the message variable
self.s.send(self.message.encode())
# Waits for a reply
self.receive_message()
def message_to_send(self):
# Defines Message to send
self.message = self.display.text
# Calls function to send the message
self.send_message()
# Note
# When I used message = input directly in send_message,
# the app would crash. So I defined message input
# in its own function which then calls the
# send function
# message_to_send is the function actually
# called by a button press which then
# starts the chain of events
# Define Message, Send Message, get Reply
def receive_message(self):
# Decodes a reply
reply = self.s.recv(1024).decode()
# Converts reply to a str
strreply = str(reply)
# prints reply
print(strreply)
class ServerApp(App):
def build(self):
box = BoxWidget()
return box
if __name__ == '__main__':
ServerApp().run()
Edit: Forgot to include the kv file
<BoxWidget>:
display: display
Button:
text: 'Hello'
on_press: root.message_to_send()
Button:
text: 'Connect'
on_press: root.connect_to_server()
TextInput:
id: display
In future iterations, I'll be replacing print statements with conditionals (ie did client one draw a card? if so client 2's opponent draws a face-down card etc).
Relatively rudimentary as it is now but there is a lot you could do from here.
Upvotes: 2
Reputation: 5405
You can use threading so you don't interrupt the main thread in kivy.
I rewrote your example a bit, so what you send from the server, will be the text of a label.
server.py
import socket
serversocket = socket.socket()
host = 'localhost'
port = 54545
serversocket.bind(('', port))
serversocket.listen(1)
clientsocket,addr = serversocket.accept()
print("got a connection from %s" % str(addr))
while True:
msg = input("> ") + "\r\n"
clientsocket.send(msg.encode('ascii'))
client.py
import socket
class MySocket:
def __init__(self,host="localhost",port=54545):
self.sock = socket.socket()
self.sock.connect((host, port))
def get_data(self):
return self.sock.recv(1024)
main.py
from kivy.app import App
from kivy.uix.label import Label
from client import *
from threading import Thread
class MyLabel(Label):
def __init__(self, **kwargs):
super(MyLabel,self).__init__(**kwargs)
self.sock = MySocket()
Thread(target=self.get_data).start()
def get_data(self):
while True:
self.text = self.sock.get_data()
class BoxApp(App):
def build(self):
return MyLabel()
if __name__ == '__main__':
BoxApp().run()
Now just run server.py in one terminal, then main.py from another
Upvotes: 3