Look Ma, No HTTP!
This is a modal window.
The media could not be loaded, either because the server or network failed or because the format is not supported.
Formal Metadata
Title |
| |
Title of Series | ||
Number of Parts | 118 | |
Author | ||
License | CC Attribution - NonCommercial - ShareAlike 3.0 Unported: You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal and non-commercial purpose as long as the work is attributed to the author in the manner specified by the author or licensor and the work or content is shared also in adapted form only under the conditions of this | |
Identifiers | 10.5446/44822 (DOI) | |
Publisher | ||
Release Date | ||
Language |
Content Metadata
Subject Area | ||
Genre | ||
Abstract |
| |
Keywords |
EuroPython 201949 / 118
2
7
8
15
20
30
33
36
39
40
45
49
51
55
57
58
63
64
66
68
69
71
74
77
78
80
82
96
98
105
107
108
110
113
115
00:00
Point cloudGoogolNetwork socketWeb browserStandard deviationClient (computing)Android (robot)Java appletWeightServer (computing)Term (mathematics)Coding theoryBitWeb applicationWeb 2.0Different (Kate Ryan album)Cartesian coordinate systemComputer configurationServer (computing)Client (computing)Multiplication signNetwork socketJava appletSinc functionConnected spaceImplementationoutputComputing platformStandard deviationWeb browserSubsetCommunications protocolLine (geometry)Frame problemGame theoryVideo game consoleSystem callComputer chess1 (number)Lecture/ConferenceJSONXMLComputer animation
04:37
Linker (computing)WindowSicoutputTerm (mathematics)Multiplication signWeb 2.0Type theoryQuicksortServer (computing)Cartesian coordinate systemSource codeValidity (statistics)Projective planeNetwork socketComputer chessProcess (computing)MaizeComputer fileClient (computing)GodXML
05:37
Execution unitReading (process)View (database)Network socketScripting languageServer (computing)Link (knot theory)Price indexComputer chessAsynchronous Transfer ModeError messageProcess (computing)CodeInformationComputer fileSoftware maintenanceLatent heatComputer chessComputer fileFunctional (mathematics)MaizeClient (computing)Web browserAreaCartesian coordinate systemMedical imagingServer (computing)Wrapper (data mining)Software developerNetwork socketInstance (computer science)Mobile appFluid staticsSocial classValidity (statistics)Front and back endsWhiteboardSingle-precision floating-point formatDirected graphBitBootingCASE <Informatik>WindowIntegrated development environmentLogicDebuggerGoodness of fitUniform resource locator
09:47
Graphical user interfaceHTTP cookieInternetworkingBookmark (World Wide Web)WebsiteForm (programming)Gastropod shellLocal ringWeb 2.0Scripting languageComputer fileView (database)Price indexNetwork socketInformationServer (computing)Process (computing)Link (knot theory)Computer chessDebuggerCartesian coordinate systemNear-ringMultiplication signComputer chessSubject indexingNumberComputer fileElement (mathematics)CountingJSONXMLComputer animation
11:09
Scripting languageInformationServer (computing)Computer fileView (database)Event horizonNetwork socketModule (mathematics)CodeAsynchronous Transfer ModeError messageProcess (computing)Line (geometry)Client (computing)Connected spaceEvent horizonNetwork socketMereologyType theoryCartesian coordinate systemData dictionaryLatent heatDifferent (Kate Ryan album)NumberUniform resource locatorFunctional (mathematics)CASE <Informatik>System callAddress spaceServer (computing)Web pageEmailInstance (computer science)InformationIdentifiabilityGame theoryMultiplication signCommunications protocolComputer chessHTTP cookieSet (mathematics)Parameter (computer programming)Element (mathematics)DatabaseReal numberData storage deviceCountingMobile appMusical ensembleOrder (biology)Integrated development environmentTrigonometryAuthenticationComputer animation
16:42
Form (programming)Graphical user interfaceLocal ringBookmark (World Wide Web)HTTP cookieTerm (mathematics)Multiplication signRight angleNumberInheritance (object-oriented programming)JSONXMLComputer animation
17:49
Scripting languageComputer chessInformationPrice indexComputer fileView (database)Network socketWhiteboardClient (computing)Clique-widthSicGame theorySocial classElement (mathematics)WhiteboardComputer chessGame theoryCartesian coordinate systemWeb pageRandom matrixElectronic visual displayComputer configurationPosition operatorPixelPositional notationSet (mathematics)Multiplication signLibrary (computing)Polar coordinate systemServer (computing)BitComputer animation
20:29
Computer chessNetwork socketInformationGame theoryEvent horizonServer (computing)Computer fileView (database)WhiteboardAsynchronous Transfer ModeError messageWebsiteSynchronizationProcess (computing)Structural loadPosition operatorCASE <Informatik>Server (computing)Modal logicClient (computing)Computer chessCountingWhiteboardStandard deviationTupleLibrary (computing)Game theoryPosition operatorStructural loadElectronic mailing listGreatest elementPhysical systemDebuggerFunctional (mathematics)Different (Kate Ryan album)AdditionLogicDescriptive statisticsLattice (order)Object (grammar)MathematicsParameter (computer programming)Graph coloringHand fanMereologySoftware testingData dictionaryRight angleSet (mathematics)System callFront and back endsPoint (geometry)Orientation (vector space)Source codeComputer animation
25:35
Computer fileWebsiteGame theoryWhiteboardView (database)InformationCoding theoryGraphical user interfaceComputer chessStructural loadEvent horizonServer (computing)Network socketLocal ringVariable (mathematics)Error messageWeb browserComputer animation
26:33
Computer fileWebsiteInformationTouchscreenEvent horizonGame theoryView (database)Computer chessNetwork socketServer (computing)WhiteboardConvex hullVideo game consoleWechselseitige InformationProcess (computing)Error messageAsynchronous Transfer ModeCodeMathematicsStructural loadDifferent (Kate Ryan album)Game theoryComputer animation
27:33
InformationFunctional (mathematics)Interior (topology)Computer fileView (database)WhiteboardComputer chessNetwork socketOrientation (vector space)Game theoryStructural loadCountingFunction (mathematics)Computer fontProcess (computing)Multiplication signClient (computing)Uniform resource locatorServer (computing)DebuggerMereologyCASE <Informatik>Drop (liquid)Functional (mathematics)Square numberGame theoryComplex (psychology)Source codeComputer animation
28:58
Game theoryCountingView (database)Computer fileWhiteboardInformationComputer chessNetwork socketOrientation (vector space)MathematicsStructural loadFunction (mathematics)Expected valueGraph coloringComputer chessCASE <Informatik>Perfect groupSquare numberMereologyWhiteboardNumberSource codeComputer animation
31:37
Hand fanMotion blurMechatronicsVideo game consoleInformationView (database)Positional notationWhiteboardNetwork socketComputer chessOrientation (vector space)Game theoryLine (geometry)Computer animation
32:25
Interior (topology)Computer chessFunction (mathematics)Orientation (vector space)WhiteboardInformationNetwork socketEvent horizonComputer fileView (database)Server (computing)Game theoryCountingProcess (computing)Asynchronous Transfer ModeError messageStructural loadPosition operatorMaxima and minimaVariable (mathematics)Positional notationGame theoryLibrary (computing)Boolean algebraEvent horizonParameter (computer programming)Client (computing)Valuation (algebra)Validity (statistics)Server (computing)Functional (mathematics)Network socketPositional notationDependent and independent variablesRight angleError messageSquare numberSet (mathematics)Position operatorWhiteboardInstant MessagingSolid geometryRepresentation (politics)CASE <Informatik>Hand fanSinc functionDebuggerPoint (geometry)Graph coloringEntire functionComputer chessElectronic mailing listObject (grammar)Wrapper (data mining)MereologyArithmetic meanLine (geometry)TuplePerfect groupComputer animationSource code
40:43
View (database)InformationDependent and independent variablesGame theoryComputer animation
41:36
Client (computing)Price indexInformationLattice (order)InfinityConvex hullDuality (mathematics)Computer chessServer (computing)NeuroinformatikWeb browserContext awarenessEvent horizonStandard deviationCASE <Informatik>SynchronizationGame theoryMultiplication signPosition operatorRevision controlClient (computing)WhiteboardLogicPhysicalismValidity (statistics)Cartesian coordinate systemUniform resource locatorThread (computing)Computer fileCountingComputer animation
44:41
InformationClient (computing)Price indexEvent horizonSoftware maintenanceNetwork socketView (database)Computer fileServer (computing)Process (computing)Asynchronous Transfer ModeModule (mathematics)Exception handlingFunctional (mathematics)CountingParameter (computer programming)Client (computing)Right angleSource codeXML
45:43
Computer fileNetwork socketInformationException handlingMenu (computing)View (database)Process (computing)Server (computing)FreewareLocal ringClient (computing)Computer chessEvent horizonWhiteboardMathematicsLetterpress printingWebsiteModule (mathematics)Lattice (order)outputAsynchronous Transfer ModeInstallable File SystemLine (geometry)Multiplication signServer (computing)Event horizonClient (computing)WhiteboardFunctional (mathematics)Graph coloringGame theoryComputer configurationNetwork socketCombinational logicPositional notation2 (number)Revision controlSystem callComputer chessPosition operatorCodeAdditionBoolean algebraHand fanExistential quantificationArithmetic meanLetterpress printingRepository (publishing)Musical ensembleRight angleInformation systemsMereologyXMLSource codeComputer animation
54:13
Coding theoryRepository (publishing)Roundness (object)VideoconferencingWeb pageLogicConnected spaceWeb 2.0Socket-SchnittstelleServer (computing)Streaming mediaDependent and independent variablesNetwork socketScalabilityCartesian coordinate systemRight angleClient (computing)Presentation of a groupMultiplication signMetropolitan area networkComplex (psychology)Single-precision floating-point formatMedical imagingBitTime zoneEvent horizonSinc functionBinary codeRaw image formatCodeLevel (video gaming)Mobile appAbsolute valueComputer virusXMLLecture/ConferenceComputer animation
Transcript: English(auto-generated)
00:05
Thank you so much for coming. Send me the bill later for that nice introduction. Thank you. So OK, so what I'm planning to do here is a little bit different.
00:20
I would like to show you a way to write a web application that I think for some reason doesn't have so much popularity. A lot of people don't consider this a valid option. So before I get into the coding, I'm going to give you just a quick introduction just to set your frame of mind so that you know
00:43
how this is going to work. So I'm going to use the Socket.IO protocol. I'm not going to explain how it works under the hood, but all I'm going to say just to set you to relax is that it runs on top of WebSocket.
01:03
So that's what I'm going to say about how it works. There is documentation for this protocol in Socket.IO. This is a mostly JavaScript-based documentation. And then there's the Python port of it,
01:20
which is on readtodocs. Just so that you don't think this is an obscure thing that nobody knows about, Socket.IO clients are available for most platforms. So obviously, the Python one, which comes into variants, you can use it in standard Python and async.io.
01:43
There's JavaScript, which is the reference implementation that was the first, which runs in the browser, or you can run in Node. And then there's a bunch of others. All the mobile platforms have it. And I'm sure there are more that I don't even know about. These are the ones that I'm familiar with.
02:03
That was the clients. For servers, as I said, there is a JavaScript reference implementation. There's the Python one. There's the Java one. And those are the three that I know. There might be more. For this, I only care about the Python one. That's the one that I use all the time.
02:23
And pretty much, it works like this. The client connects to the server. And unlike with HTTP and REST and those solutions, it's a permanent connection. It doesn't end. So it stays connected. So each time a client joins or leaves, the server detects that.
02:44
And then it gives your application a chance to do something about it. This one is probably obvious. The client, since it's connected at any time, it can invoke an endpoint in the server. The server exposes an API that the client can use.
03:02
But the one that's probably you're not used to is that the server can also invoke endpoints in the client. The client also exports an API. And the server can make calls into it. So basically, when you design an API for this, it's a double-sided API, if you will,
03:23
where you design endpoints on both sides. And in fact, when the client connects to the server and they're connected, there's really not much of a difference between a client and a server. Both can talk to the other side. And the lines are blurred a little bit.
03:40
The only main difference between clients and servers is that there's one server and many clients. And for that reason, the server, when it invokes endpoints in the client, it has superpowers. And it can decide if it invokes an endpoint in one specific client.
04:01
Maybe all of clients, it can broadcast. And then all the clients get this endpoint invoked. Or it can select a subset, whatever makes sense for the application, and only invoke an endpoint in those. OK? So now we know pretty much everything there is to know about this. So with this, I'm going to build a multiplayer chess
04:23
game. I'm going to try to get as far as I can here in one hour. And if I don't make too many typos, I think there's hope. If not, we'll see. So in my console, I created sort of a starter project
04:43
with some boilerplate so that I don't waste a lot of time. So I'm going to very quickly show you what I have. On the Python side, in terms of dependencies, there are four dependencies, the socket.io server and client,
05:02
UVcorn, which is a very nice async.io web server. That's what I'm going to use for this application. And then there is a chess package that I'm going to use to validate moves and that type of thing. And then finally, there is WatchGod, which is a little process that watches the source code.
05:25
And any time I make a change and save the file, it'll restart the server. So if you're using Flask, pretty much like the reloader in Flask, so that I don't have to restart it every time by hand. So server.py is going to be my server.
05:41
There's nothing in it. So far. And then I have the front end, which is in the public folder. And here I have a one HTML file, which has a title, and then four dependencies for the JavaScript side, which
06:00
are jQuery, the socket.io client for the browser, and then two related to chess, one to show chess boards, and one to do move validation as well on this side. And that's it. And then I have an app.js, which is what I'm going to write. Which if I remembered, yeah, it is an empty file.
06:24
So this doesn't exist yet. And then what else do I have here? I have some CSS, some images. These are for the dependencies, for the chess stuff. And that's pretty much it.
06:42
So I'm going to start my reloader, which basically I'll tell it to watch this function, the main function in the server. And now it's going. So even though there's nothing yet, then when I start putting stuff, it'll reload.
07:00
So let's see. So I'll leave it there. In case I make a mistake, I'll see it there. So I'll just leave a little bit of that window. I'm going to activate environment here. So let's create a server.
07:23
So sorry, not asyncio, socketio. I'm going to import asyncio and uppercorn. And then I'm going to create the socketio instance. It's going to be the async flavor.
07:42
So this, once again, you don't have to use asyncio if you don't want to. You can use, there's a class called server, just server, that implements the same functionality but for traditional Python. And uppercorn is a ASCII server. For those who are not familiar, ASCII
08:02
is the async improvements over Wysgi. So there's a specification for ASCII and uppercorn supports that. So to connect the socketio server with uppercorn, socketio provides a wrapper that converts this application into an ASCII compliant application.
08:23
There are many other wrappers for other things. You can do Wysgi and a bunch of others, tornado, et cetera. So I'm going to create an application with the ASCII wrapper and pass my socketio instance to it. And now I can just run the application.
08:50
And it restarted and everything's looking good. So I have a bunch of static files, these front end files in the public folder. As a convenience, this ASCII wrapper
09:02
allows me to expose those files so that during development I can easily serve them to clients, to browsers. And of course, now I have to admit that I lied because this is going to use HTTP. The files are going to be served over HTTP.
09:21
But none of the application logic will use HTTP. So this is going to be the slash URL. It's going to map to the public folder just like that, I think. Oh, I'm sorry. Static files.
09:45
Very good. So if I now run this, I'm getting the empty front end. So this is looking pretty good. So before I get into the chess stuff, which you may or may not
10:01
find interesting, I'm going to show you something that I hope will interest most of you. This is something that is incredibly difficult to do with HTTP. I want to show how many users are using the application at any given time. So think about it. How would you do it with HTTP? And if I think about it, I get a headache
10:21
because it's near impossible. It's very hard. So let me show you how to do that this way. So I'm going to start with the index file. I'm going to add a place to show this number, the number of users. So an H2, and it's going to be something like that.
10:48
Because I'm going to dynamically change the 0 here, I'm going to put it in its own element. And I'm going to call it user count, let's say.
11:02
So then I'm going to use jQuery to change this when appropriate. It should be very good. So actually, I'm going to do the client. So the client needs to connect, right? I haven't done any connections yet.
11:21
So in the client, public js app, I'm going to create, in the same way I did it for the server, I'm going to create an instance of Socket IO for the client. In the client, it's actually very easy. You just do that, and that's it. IO comes from the Socket IO client.
11:42
It's the main function that creates the instance. And if you don't pass any arguments, it connects to the same server that the page came from. So it uses the URL in the address bar. And you don't have to specify a connection URL. So now this client now connects, and it remains connected.
12:02
So in the server, the server is going to detect that, and it's going to want to notify my application. And whenever there's these types of notifications, Socket IO calls them events. So I'm going to create an event,
12:20
and I'm going to call it connect. And the name of the event is important. Basically, the sio.event decorator uses the name to know which events are handlers for different aspects of the application. There are other ways to create events.
12:42
If you don't want to be forced to use a given name for your functions, there are other ways to create them, but I find this the most convenient. And the connect handler gets two arguments. The SID, or session ID, is part of the Socket IO protocol.
13:03
Each time a client connects, it gets assigned a unique identifier. This is called a SID. So I get the SID number assigned to this client that is connected. And then Environ is basically the request. So you get information about headers and whatever else,
13:21
cookies, all that stuff. And you can use this for authentication, those type of things, which I'm not going to do here, but it's there in case you need to do that. That is a dictionary, and it's formatted according to the WSGI specification. So anyway, here I still don't have the chess stuff going,
13:46
but I know that for each user, I want to assign the user to a game. So I'm going to have some storage, which for this exercise, it's going to be an in-memory dictionary.
14:00
For a real application, it might be a database, it might be a Redis, that type of thing. But I'm going to simplify. And here, I'm going to just write something for this user. So for now, I don't have a game yet. I'm going to put a game later on. For now, I'll just put that.
14:20
And then, likewise, there's going to be a disconnect. The disconnect gets the SID of the user that's going away. And what I can do here is remove it. Oops, that's it, right? So this is the cool part. I want to have an endpoint in the client
14:40
that updates the number of connected users at any time a user comes or leaves. And now I start getting into the async part, so I need to make this an asynchronous function. And I'm going to do an emit, which is the method that makes a call into the other side.
15:01
Both sides can do emits. So I'm going to call this one userCountChanged. How about that? And then, as an argument, I'm going to pass how many users I have. And then, oh, okay.
15:28
So you don't have to worry too much because when I save, I'll be told, if I made a mistake, I'm prepared for this. So don't worry, don't, you know,
15:41
don't worry too much. And this one still needs the async. There we go, okay? So what I'm doing here is invoking an endpoint in the client, which I didn't write yet, and passing an argument, which is the number, okay?
16:03
So let's go to the client. In the client, the syntax is a little bit different. userCountChanged, and then there's a callback that takes the number. There we go.
16:21
And then when this is invoked, I can take the userCount element, which was the span that had the zero, and then set a new text to the number.
16:41
And fingers crossed, let's see if it works. So now we get one. So I'm going to start another one. Now it's two. So I see if I can do this. Maybe some of you don't believe me.
17:02
So now you can see two at a time. I'm going to start a third one, right? So look at the number three on the left. I'm going to go ahead and close one of the two that I have on the right. You see, this is so easy and so amazing.
17:22
And if you had to do this with HTTP, I would not even know how to start, right? It's super messy. It will be super messy. So anyway, this is basically the way things work. So I'm going to continue with this, and this is going to be the style.
17:42
I'm going to start deciding on things that I need to call on the other side and then go to the other side and implement them. So I'm going to spend maybe a minute putting up a chessboard on this page. Hope this isn't too boring. So this is going to be a board element.
18:03
And then in the application, the game is going to be of class chess. So I need two things. The game is going to be what validates moves, and then there's going to be a board, which is what displays to the game.
18:21
And this is chessboard. And this one takes the ID of the element where the board goes. So let's see. So that's the board, it's kind of big.
18:40
Let's make it a little bit smaller. Let's make it 400 pixels. Okay, that's better.
19:00
Okay, so we have a board. I'm going to set some options in this board. So first, the position. I'm going to set it to whatever the game is set to, which is going to be initially the initial position.
19:21
For those who don't know, there's a notation for chess positions that's called FEN or F-E-N. It's a standard position. It records everything about a chess position. So basically that's what these libraries use. So I'll set it to that. And then I'm going to make the pieces draggable
19:43
so that we can start moving things around. So now I get pieces, I can move, but there's nothing to validate yet. So now every user gets a board. So what I want to do now is I want to assign users
20:04
as they come into the application to a game. And I don't want to spend a lot of time on that since it's unrelated to the topic. So I'm going to do something simple. Each pair of users, when they connect, the first one will be white, the second one will be black,
20:21
and then when I get another one, I open a new game and then I keep doing it that way. So as the user comes, I do white and black and white and black and so on. So let's do that in the server. So I'm going to have a list of games in addition to a list or a dictionary of users.
20:42
And for a game, I'm going to use a dictionary with the ID, the SID of the white player. So this, for example, will be setting up the game for the white player. So I set white to the SID of the connecting user.
21:05
The black, I don't have it yet. I need to wait until someone else connects. So I'll set none. And then I'm going to set up a board which will be the chess logic, which I'm going to get from this package chess.
21:22
So chess board. And that's a game. So this is going to be the case of a white, when the player that goes to the white side joins. So this is going to apply when either I have no games,
21:43
so the first player ever, or if the last game has a black player already assigned. Right, so in that case, we have this.
22:02
And then I'm going to add it to my list, okay? And then the black case, my game is going to be the last game that I have in the list. I'm going to set the black player to the SID.
22:23
And I think that's it. I can put the game now. And then I assign the dictionary to the user. So there's going to be two users in the system that point to the same dictionary, right? So these are the two users that are playing the game against each other. So I need now to tell the front end
22:42
that the game is ready to go. So I'm going to use a new endpoint that I'm going to call new game. I'm going to pass the FEN position, the description of the board to the client.
23:00
So that is going to be gameboard.fen. You can see that all these libraries use the same standard. They're all kind of the same in that respect because this is a widely available standard for chess. And then this one, the user count changed. I just said emit, and it went to everybody, right?
23:23
But this one, the new game, I want to send only to the user that connected. So I send it by using the SID that was assigned to that client. So I send it to that person only. One more thing that I can do now that I think about it
23:42
is the player that gets the black, typically on a game of chess, the black pieces are at the bottom for the black player. So I'm going to just very quickly set the caller, and then here, I'm going to send two arguments,
24:04
which in the Python side, you have to send us a tuple. So send the FEN and the caller that was assigned to that player so that then the front end can turn the board around if necessary. Define game, where is it?
24:22
Yeah, games. Okay, very good. So on the client side, now we need to implement new game. This is the end point that starts the game. And this function gets the FEN and the caller,
24:45
and what I can do here is I can take the game object and set the FEN to the position that server sends. So this library uses load to set a position.
25:03
And then this is in the hidden part, the package that does the logic. I need to update the board as well. So for the board, I'm going to say, and of course, it uses a different API for this. I'm gonna do it like that, it uses the position.
25:23
And then the board also has orientation, and you can set it to white or black. So I set the caller, and there we go. So now let's bring our two browsers.
25:43
I think I had an error. Where is it, did you see it? In the Python side? 21, oh, there we go, sorry.
26:08
I wasn't that well prepared, it looks like. Okay, let's see if that works better. No, there's still something going on.
26:32
Sorry, I don't see it. The Ls?
26:40
No, this is fine. It's probably in the other side.
27:02
Oh, I didn't save, really. Okay, let's see. Sorry about that, yeah. So okay, so now I refresh too, and the second one gets to black, okay?
27:24
So now players are assigned to games. If more players joins, they're gonna get different games, right, new games. So what I want to do now is to make a move, and this is actually the most complex part of this.
27:42
So I'm going to make a move on one of the front ends, and then that client is going to invoke an endpoint in the server. The server will validate the move, and then the server needs to invoke an endpoint in the other front end, the one that's playing with the first, to update the move, right?
28:01
And then the process will reverse, and then the two clients will switch places, and that's how we get the game going. So let's see how that works. The chessboard package has an onDrop that you provide a callback,
28:21
so each time I move a piece and I drop it in a different place, then it will call this function, and it sends me the two locations, start and end of the piece that was moved. So what I can do here is try to move,
28:47
and this is actually very easy. So you just give it the start and end squares, and these are, if you look at the, in case you're not familiar, you can probably see, or maybe not,
29:01
but each square has a coordinate which is a letter and a number. So the from and the to are each two characters, a letter and a number, and that basically determines from where to where you're moving. It's actually pretty simple. So I make the move, and then I can check if the move was accepted.
29:22
So if the move was not accepted, I can, and this is part of this package API, I can return snapback, and that will undo the move as invalid. The other thing that I need to do is I need to make sure that not only that the move is valid,
29:41
but it's valid for the color of the pieces that this player is assigned, right? So we don't want the white player to make a legal move for the black. So for that we need to check that it is the turn of this player to move, not the other player.
30:00
So for that, let's see, we can do turn, actually, no, it's easier. I know the color already. So I can put this in a variable.
30:23
There we go. So now we know the piece color for this player. So if, and then this board, the chess package returns,
30:41
it returns a letter, either W or B for white or black. So what I can do is check the first letter, which is how you can make sure that it's the same, it's the player's turn. And if it's not, once again, snapback.
31:07
Okay, what did I do now? Hold on.
31:21
I got it, I got it. More? Perfect. Perfect. So this, now if I try to move,
31:42
this is it, what happened now?
32:01
Is it? Okay. So my linter isn't doing, yeah, line.
32:22
So let's put this on white, the other one on black. So now if I move the black, not accepted, if I make an invalid move, not accepted, the valid move is accepted. And this one cannot move, even if it's a valid move for the white,
32:42
because it's the black. So now that I have a valid move, I can send it to the server. So from the client, it's the same thing. Emit, I can call it move made. And I pass the two coordinates, from and to.
33:08
Of course. And then on the server, I write another event, move made.
33:22
The events on the server always receive the SID of the client that sent the event as the first argument, and then you get the arguments for, the arguments that came with the event. So here we have from and to, and actually from is served in Python. So let's do from square to square, something like that.
33:46
So here I need to validate again that the move is valid. Always when you do validation, validation done in the client is for user experience, but the real validation is done in the server. So we need to repeat the same thing.
34:01
So actually, let's get the game. So that's the game. The turn here is game board.turn, and here, this chess library uses a boolean, so true means white, false means black.
34:22
So I'm gonna do white, oops, else black. And then I need to know whose color is this player playing.
34:40
So I can do white, if game white matches the SID for the event, and if not, it's gotta be black, right? So if turn matches the piece color, then we're good to go, at least on the turn aspect.
35:01
This player is allowed a move right now. So the next thing is to try to make the move and see if it flies. So in the chess package, the easiest way that I found to make a move is to use this notation called UCI, which is actually
35:22
a concatenation of the two coordinates, so four characters. And then once you have a move object, you can check if move is in the list of legal moves
35:40
for the game at that point. So if all of this is true, then the move is valid, and then you can actually apply the move. There we go. So now the move is made. So there are a few considerations here.
36:02
There might be an invalid move or the player trying to move when it's not their turn. So in all those cases, we need a way to tell the front end that the move was not accepted so that the front end can then get a new move from that user. So this is another cool feature of Socket.IO,
36:24
which kind of matches what you can do with REST or HTTP, where you send a request, you always get a response, so you know if that request worked, right? So when you emit an event, you can also get a response, which I haven't used yet. So on the Python side, you send a response
36:42
by returning something from the function. So you can return the position. So if the move was not accepted, I'm going to return the same position and the board in the front end will revert to whatever it was before.
37:01
So, okay, no errors, hopefully. And then on the client, this return value is given, because this is JavaScript, you get it as a callback. So you can add a function at the end after your arguments
37:21
and this function gets, whatever you return, it gets it as argument. And if you want to return multiple arguments, you return a tuple from Python. So here what I can do is I can basically set the,
37:44
we have that, actually I do have it, yeah. It's these two lines, I need to repeat this. So I'm going to take these two and I'm going to write a little wrapper function.
38:00
Update like that. And then here, I don't have to repeat myself. So this is going to undo the move if it's invalid. So the part that remains is now the client
38:25
now has an updated board. But the other player now needs to know what the move is. So if, after all the checks, we apply the move, now we can emit, I'm going to call it opponent move.
38:48
And I could send the from and the to squares, but really I don't have to. I can send it to the entire board since it's really a line. It's actually a pretty short representation. So I'm going to send the fan of the updated position.
39:10
But this needs to go to the other player. So I need to find out who that is.
39:21
So the other player is going to be game white. If we are black. Else, this is going to be game black, right?
39:45
I'm not using it yet, perfect. So then to other player. So this is a private message that goes to the other player in that game. Okay, so opponent move.
40:01
So now back in the client, opponent move. This also receives a fan notation for the board. And all I need to do is once again update the board
40:21
because everything else happens on its own, right? When the other player tries to move, we are going to reevaluate if it's their turn to move. So all I need to do is update the board. And one of the things that this fan notation has is who's turn is. So that's one of the things that are included in the representation.
40:43
So based on past experience, I don't trust myself. I bet I made some mistake here, but let's see. So, oh, there you go, okay?
41:02
So yeah, so, but now the real challenge is the black response and they also get it. So basically we're done, we have a game here, right?
41:21
So the things that I'm not gonna do here because they will be boring is detect when the game ends. All of that you can do fairly easily. It doesn't have any challenge. One thing that I didn't mention though that I'm gonna show you, this callback thing that I did, this return, returning the position,
41:42
not only applies to invalid moves, but chess has some complex moves where typically when you're playing with a physical chess board and chess pieces, some moves you need to move two pieces. For example, one is called castling, right? So in computer chess, the convention is
42:02
that you don't move the two pieces. You only move one and then the computer knows that the only possible thing is that the other piece needs to move two. So the computer moves the second piece. So by returning the updated board position, I basically, I solved that and I'm gonna show you,
42:22
I'm gonna just play a quick game against myself just enough to get to a position where I can do a castling. So here, the white king and the white rook are gonna do that, but according to computer chess standards,
42:42
you only move the king. And then because I'm returning the updated position, the chess package in Python who understands the logic of that move, it moved the rook as well. So returning that position refreshes the board. So even if I didn't have chess validation logic
43:02
in the client, I will still get the correct move because the server is updating it. Make sense? So time, okay. So what I wanna show you now is that you can do a Python client as well.
43:25
So I'm gonna just create a new file. So this is gonna be a client that's gonna be playing chess as well. So somewhat similar.
43:45
I'm going to create. So this one, the server I showed you, the asyncio version, for this one just for fun, I'm gonna do a traditional, standard Python, no asyncio since they kinda work the same.
44:02
So I'm going to connect. In this case, I don't have context like the browser has, so I need to give it the location of the server. And then because this is all event-based,
44:20
there's really nothing else that I need to do in the main thread of the application. So all I do is wait. And now events are going to start showing up. So for example, remember this user count changed event that was invoked every time a user joined or left.
44:45
So we can do that one. User count changed, and then new count. On this side, you don't get to see it because there's only one client, right? We're looking at this from the client side. So you just get the arguments.
45:01
And here we can just print something like that. So if I didn't make any mistakes,
45:23
oh, I did make a mistake. Actually, I forgot to run the main function. There we go. It's restarting. That's not my fault.
45:41
So let's bring, so let's say, I'm gonna start one more here. So each time someone comes or goes, the Python function runs, and I get a chance to do something regarding that event.
46:02
You see? So we can actually very easily complete the game. So we had a new game, and this was fan and color.
46:24
So here I can have a board, there we go. And the board is set with set fan.
46:45
And then for the color, I'll just put it in a variable so that I know later when it comes the time to make a move. So global piece color, there we go.
47:04
So now if we get white, now it's our turn to move on this Python client. But I don't know, right? It could be white or black. So what we can do here is just make a function
47:21
to move if it is my turn. So turn is going to be white, if board.turn else black, remember that in the Python chess package,
47:42
it's a Boolean. So I'm gonna, just to make it more readable, I'm gonna switch it into white and black. And then if piece color equals turn, that means that we need to move. So I can print the board. Luckily, there's a handler that prints
48:01
a text version of the chess board. When I print it, I can show the color so that you know who you play with. And then just prompt for a move, just like that.
48:23
So we can get the move with this UCI notation for characters. So the first move will be E2, E4. That will be the first move that I've made with the whites, something like that. And then I'm not gonna worry about validating because the server validates anyway.
48:40
So, and then the server will let me know if the move was invalid. So move made was the event. And then I needed to pass the from and the to. So here I need to split this four character move into two parts.
49:02
The first is the first two characters and the second is the third and the fourth. And this is when the server receives this, validates, notifies the other side and then returns in that callback to the JavaScript client,
49:21
it returned the updated board. So for this, I can do something like this and then put a function, which will be kind of similar to JavaScript, but we Pythonists, we hate this, right? So there's a more Pythonic way to do this, which is instead of emit, you can use call.
49:43
And call combines an emit with waiting for the callback. And then you get the response, like you're making a function call, but it's a function call across to the other side. So now what I can do is I can take this board
50:02
and update it this position. This position could, if the move was valid, now it's the turn of the other player. But if the move was invalid, it's my turn again. So what I can do here is I can say, again, move if it's my turn, right?
50:22
So then if it's not my turn, then this is gonna end and eventually we're gonna get an event when the other side moves, which is actually the last thing that we need to do. Opponent move, what's it called, I think. And it receives, once again, the fin.
50:43
And when I get that, all I need to do is same thing, update the board, and then if it is my turn to move, prompt for a move. Let's see, okay, something's not working.
51:07
What is, what did I miss here? Is that fin?
51:30
Great, okay, I'm gonna debug this. Exciting.
51:43
Let's see what I'm getting. Okay, there we go. So I'm gonna make a move, but let me,
52:03
let's put this, this JavaScript one on the other side. So this is the black for that game. It might be, yeah, it might be, let's see.
52:21
This is good. So I moved the JavaScript client and now it received here the move.
52:41
I can respond, E7, E5, and, you know, now Python and JavaScript playing with a Python server in the middle. So I think, there was one more thing that I wanted to show, only there was time.
53:01
And there isn't, I think. I assume you have some questions, so I'm gonna leave like five minutes for questions. So I'm just gonna tell you what I was gonna do next. The idea was add another option where if you're playing a game here, you can specify that you wanna watch
53:21
all the other games that are going on. So that will basically put you in a, what Socket.IO calls a room. So there was a room called Watchers. So all the clients that want to watch games ask the server to be put in that room. And then the server, each time there's a move, in addition to all the notifications
53:40
to the white and the black players, it sends a notification to the Watchers room so that the client can show all the boards. So basically that was the idea that I don't think I have time to complete, ask a bunch of HTML to set up that. So it will be a little boring
54:01
and I will make mistakes and so on. But anyway, what I can do is I can actually complete it and then all this code that I wrote, I'm going to commit to this. This is a GitHub repository. So if you wanna play with it, after the talk, I'll commit it there
54:20
and you can play with it. So yeah, I'll leave it there and I think we have five minutes for questions. Thank you. Yes, so as Miguel said, we have five minutes for questions.
54:43
You can line up at one of the two mics here or if you're in the middle somewhere, you can just put up a hand and I'll come around with a microphone. Hi. Hello. Thank you. That was a brilliant talk. One question for me that may be a bit big question
55:01
to answer, but can you tell us a bit more about the drawbacks maybe of using WebSockets over HTTP, maybe code complexity, scalability, I don't know. Drawbacks of using WebSocket in general. So one thing that I will say
55:23
is that WebSocket is not appropriate for an application that is not a single page application, right? Because each time the page changes, all the connections are ongoing, they're broken. Basically, all the connections are disconnected.
55:41
So each time you switch the page, you have to connect again, so it's impractical and that applies to Socket.IO as well. I would not use it for something that is not a single page app. WebSocket on its own doesn't have reconnection logic. So if you are on your phone
56:00
and you're connected via WebSocket and somehow you lose Wi-Fi, there's nothing in the WebSocket that will reconnect you. So it's kind of annoying. Socket.IO does have reconnection logic though. So if you're using this and you pass through it in a no signal zone, no Wi-Fi zone,
56:23
when you get the signal back, you will be automatically reconnected. So this has, in my opinion, some interesting advantages over plain WebSocket.
56:44
Luke. Thank you for the presentation. It was a really great overview. In there, you demonstrated that you can get a response for a WebSocket event. Is that the feature of WebSockets or a feature of Socket.IO? This is a feature of Socket.IO, not a feature of WebSockets, no.
57:02
Okay, thank you. Yeah, in fact, the WebSocket API, it's very basic, pretty bare. There isn't much you can do except sending and receiving. You know, that's pretty much it. So yeah, this does a lot of stuff on top.
57:28
Thanks for the talk. One question. Would you ever find yourself in using WebSockets over Socket.IO? Would you find the use for just WebSockets and not to Socket.IO?
57:42
I never use WebSocket alone. It's been years since I've used it on its own. This is way more useful, much more high level. So yeah. And another question. Does it support binary data, raw binary data?
58:01
Socket.IO supports binary data, yes, absolutely. You can send images. You can send a video stream, for example, at a different talk a couple years ago. I showed how to record audio and then stream it from the client that's recording it to the server, for example. So audio data, images, video, all of that works, yeah.
58:22
Thank you. Any more questions? Going once, going twice. All right, looks like there's no more. So let's have another round of applause for Miguel. Thank you. Thank you so much. Thank you.