Skip to content

Instantly share code, notes, and snippets.

Created December 22, 2012 03:02
Show Gist options
  • Save anonymous/4357294 to your computer and use it in GitHub Desktop.
Save anonymous/4357294 to your computer and use it in GitHub Desktop.
server
local socket = require "socket"
require 'vendor/lube'
local Player = require 'player_server'
local Level = require 'level_server'
local Gamestate = require 'vendor/gamestate'
-- begin
local udp = socket.udp()
-- normally socket reads block until they have data, or a
-- certain amount of time passes.
-- that doesn't suit us, so we tell it not to do that by setting the
-- 'timeout' to zero
udp:settimeout(0)
-- unlike the client, the server has to be specific about where its
-- 'bound', or the poor clients will never find it.
-- thus while we can happily let the client auto-bind to whatever it likes,
-- we have to tell the server to bind to something known.
--
-- the first part is which "interface" we should bind to...a bit beyond this tutorial, but '*' basically means "all of them"
-- port is simpler, the system maintains a list of up to 65535 (!) "ports"
-- ...really just numbers. point is that if you send to a particular port,
-- then only things "listening" to that port will be able to receive it,
-- and likewise you can only read data sent to ports you are listening too.
-- generally speaking, if an address is which machine you want to talk to, then a port is what program on that machine you want to talk to.
--
-- [NOTE: on some operating systems, ports between 0 and 1024 are "reserved for
-- privileged processes". its a security precaution for those system.
-- generally speaking, just not using ports in that range avoids a lot of problems]
udp:setsockname('*', 12345)
--remember to make world part of a class
local players = {} -- players[player_id] = player
local levels = {} -- levels[level_name] = level
-- We declare a whole bunch of local variables that we'll be using the in
-- main server loop below. you probably recognise some of them from the
--client example, but you are also probably wondering what's with the fruity
-- names, 'msg_or_ip'? 'port_or_nil'?
--
-- well, we're using a slightly different function this time, you'll see when we get there.
local data, msg_or_ip, port_or_nil
local entity, cmd, parms
local update_ticker = 0
local last_update = os.time()
local dt = 0
-- indefinite loops are probably not something you used to if you only
-- know love, but they are quite common. and in fact love has one at its
-- heart, you just don't see it.
-- regardless, we'll be needing one for our server. and this little
-- variable lets us *stop* it :3
--local running = true
-- the beginning of the loop proper...
function server_print(...)
print(...)
io.flush()
end
function love.load()
Gamestate.Level = Level
server_print("Beginning hawkthorne server loop.")
--require("mobdebug").start()
end
function love.update(dt)
-- this line looks familiar, I'm sure, but we're using 'receivefrom'
-- this time. its similar to receive, but returns the data, sender's
-- ip address, and the sender's port. (which you'll hopefully recognise
-- as the two things we need to send messages to someone)
-- we didn't have to do this in the client example because we just bound
-- the socket to the server. ...but that also ignores messages from
-- sources other than what we've bound to, which obviously won't do at
-- all as a server.
--dt = os.time() - last_update
--last_update = os.time()
--gotta fix this eventually so that each level gets their correct dt
for level_name,level in pairs(levels) do
level:update(dt)
end
--
-- [NOTE: strictly, we could have just used receivefrom (and its
-- counterpart, sendto) in the client. there's nothing special about the
-- functions to prevent it, indeed. send/receive are just convenience
-- functions, sendto/receive from are the real workers.]
data, msg_or_ip, port_or_nil = udp:receivefrom()
if data then
io.flush()
-- more of these funky match patterns!
entity, cmd, parms = data:match("^(%S*) (%S*) (.*)")
if cmd == 'keypressed' then
local button = parms:match("^(%S*)")
local player = players[entity]
local level = player.level
level = Gamestate.get(level)
player.key_down[button] = true
if level then level:keypressed( button, player) end
print("keypressed:"..button)
elseif cmd == 'keyreleased' then
local button = parms:match("^(%S*)")
local level = players[entity].level
level = Gamestate.get(level)
local player = players[entity]
player.key_down[button] = false
player.key_down[button] = false
if level then level:keyreleased( button, player) end
print("keyreleased:"..button)
elseif cmd == 'keydown' then
-- local button = parms:match("^(%S*)")
-- local level = players[entity].level
-- local player = players[entity]
elseif cmd == 'update' then
--sends an update back to the client
local level = parms:match("^(%S*)")
if level ~= '$' then
assert(level,"Must update a specific level")
levels[level] = levels[level] or Gamestate.load(level)
levels[level].nodes = levels[level].nodes or {}
--update objects for client(s)
for i, node in pairs(levels[level].nodes) do
if node.paint then
local objectBundle = {level = level,
x = node.position.x,y = node.position.y,
state = state,position = node.animation and node:animation().position,
direction = node.direction,
name = node.name,
type = node.type,
}
udp:sendto(string.format("%s %s %s", i, 'updateObject', lube.bin:pack_node(objectBundle)), msg_or_ip, port_or_nil)
end
end
for i, plyr in pairs(players) do
local playerBundle = {id = plyr.id, level = plyr.level,
x = plyr.position.x, y = plyr.position.y,
name = plyr.character.name,
costume = plyr.character.costume,
state = plyr.character.state,
position = plyr.character:animation() and
plyr.character:animation().position,
direction = plyr.character.direction}
--print("id: "..playerBundle.id)
-- print("x: "..playerBundle.x)
-- print("y: "..playerBundle.y)
-- print("position: "..playerBundle.position)
-- print("state: "..playerBundle.state)
-- print("direction: "..playerBundle.direction)
-- print("name: "..playerBundle.name)
-- print("costume: "..playerBundle.costume)
-- print()
--
udp:sendto(string.format("%s %s %s", i, 'updatePlayer', lube.bin:pack_node(playerBundle)), msg_or_ip, port_or_nil)
end
end
--update players for client(s)
elseif cmd == 'register' then
server_print("registering a new player:", entity)
server_print("msg_or_ip:", msg_or_ip)
server_print("port_or_nil:", port_or_nil)
server_print()
players[entity] = Player.new()
players[entity].id = entity
players[entity].level = 'overworld'
elseif cmd == 'enterLevel' then
local level = parms:match("^(%S*)")
players[entity].level = level
Gamestate.switch(Gamestate.get(players[entity].level),nil,players[entity])
elseif cmd == 'unregister' then
server_print("unregistering a player:", entity)
server_print("msg_or_ip:", msg_or_ip)
server_print("port_or_nil:", port_or_nil)
players[entity] = nil
elseif cmd == 'quit' then
running = false;
else
server_print("unrecognized command:'"..(cmd or 'nil').."'")
server_print()
end
elseif msg_or_ip ~= 'timeout' then
error("Unknown network error: "..tostring(msg))
end
socket.sleep(0.01)
end
--server_print("Thank you.")
-- and that the end of the udp server example.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment