added the playerMap lua module
parent
6e0a50b549
commit
70fe66eb98
@ -0,0 +1,214 @@
|
|||||||
|
-- This generates a json file that contains the position of
|
||||||
|
-- all the players on your server. It was inspired by the livemap
|
||||||
|
-- module by "Michael Fitzmayer" <mail@michael-fitzmayer.de>.
|
||||||
|
-- I rewrote this entire thing from scratch to work with 0.7 and hook
|
||||||
|
-- into the event handling system in a proper way.
|
||||||
|
-- My future plans for this will turn it into a nice per-player stream
|
||||||
|
-- of data, with separate update schedules per player and better
|
||||||
|
-- on-change handling of information deltas. That will work into
|
||||||
|
-- a few other ideas that I have with eater.
|
||||||
|
|
||||||
|
jsonInterface = require("jsonInterface")
|
||||||
|
customEventHooks = require("customEventHooks")
|
||||||
|
|
||||||
|
local playerMap = {}
|
||||||
|
playerMap.map = {}
|
||||||
|
|
||||||
|
playerMap.updateInterval = 1000
|
||||||
|
|
||||||
|
-- currently unused, it'd be nice to have separate update and flush
|
||||||
|
-- intervals. That way other events updating things will propagate faster.
|
||||||
|
playerMap.flushInterval = 1000
|
||||||
|
playerMap.fileName = "PlayerMap.json"
|
||||||
|
playerMap.timer = nil
|
||||||
|
|
||||||
|
function playerMap.updateAllPlayers()
|
||||||
|
for pid, player in pairs(Players) do
|
||||||
|
playerMap.updatePlayer(pid)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function playerMap.save()
|
||||||
|
jsonInterface.save(playerMap.fileName, playerMap.map)
|
||||||
|
end
|
||||||
|
|
||||||
|
function playerMap.updateTimer()
|
||||||
|
playerMap.updateAllPlayers()
|
||||||
|
playerMap.save()
|
||||||
|
tes3mp.StartTimer(playerMap.timer)
|
||||||
|
end
|
||||||
|
|
||||||
|
function OnPlayerMapUpdateTick()
|
||||||
|
playerMap.updateTimer()
|
||||||
|
end
|
||||||
|
|
||||||
|
function playerMap.updatePlayer(pid)
|
||||||
|
if not playerMap.canUpdatePlayer(pid) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local player = Players[pid]
|
||||||
|
|
||||||
|
-- Apparently this player did not login or register yet, so we won't
|
||||||
|
-- plot them.
|
||||||
|
if playerMap.map[player.accoundName] == nil then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
playerMap.updatePlayerLocation(pid)
|
||||||
|
playerMap.updatePlayerRotation(pid)
|
||||||
|
|
||||||
|
playerMap.map[player.accountName].isOutside = tes3mp.IsInExterior(pid)
|
||||||
|
playerMap.map[player.accountName].cell = tes3mp.GetCell(pid)
|
||||||
|
end
|
||||||
|
|
||||||
|
function playerMap.canUpdatePlayer(pid)
|
||||||
|
return Players[pid] ~= nil and Players[pid]:IsLoggedIn()
|
||||||
|
end
|
||||||
|
|
||||||
|
function playerMap.updatePlayerLocation(pid)
|
||||||
|
playerMap.updatePlayerOutsideLocation(pid)
|
||||||
|
playerMap.updatePlayerInsideLocation(pid)
|
||||||
|
playerMap.updatePlayerTransitionLocation(pid)
|
||||||
|
end
|
||||||
|
|
||||||
|
function playerMap.updatePlayerRotation(pid)
|
||||||
|
playerMap.setPlayerRotation(pid,
|
||||||
|
tes3mp.GetRotX(pid),
|
||||||
|
tes3mp.GetRotZ(pid))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- We are outside, this is the happy path
|
||||||
|
function playerMap.updatePlayerOutsideLocation(pid)
|
||||||
|
if not tes3mp.IsInExterior(pid) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
playerMap.setPlayerPosition(pid,
|
||||||
|
tes3mp.GetPosX(pid),
|
||||||
|
tes3mp.GetPosY(pid),
|
||||||
|
tes3mp.GetPosZ(pid))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- We are inside and have just logged in, this is the worst path
|
||||||
|
function playerMap.updatePlayerInsideLocation(pid)
|
||||||
|
if tes3mp.IsInExterior(pid) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Player has already logged in and had a placeholder set
|
||||||
|
if playerMap.map[Players[pid].accoundName].x ~= nil then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
playerMap.setPlayerPosition(pid,
|
||||||
|
tes3mp.GetExteriorX(pid),
|
||||||
|
tes3mp.GetExteriorY(pid),
|
||||||
|
0) -- cell exteriors have no z position
|
||||||
|
end
|
||||||
|
|
||||||
|
-- We are inside but we were outside recently, so we can set out location to wherever
|
||||||
|
-- the door was. This is an okay path.
|
||||||
|
function playerMap.updatePlayerTransitionLocation(pid)
|
||||||
|
-- Player is outside so we don't need to be here
|
||||||
|
if tes3mp.IsInExterior(pid) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Last location was not outside so we can't save whatever overworld position we have anymore
|
||||||
|
if not playerMap.isOutside(pid) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
playerMap.setPlayerPosition(pid,
|
||||||
|
tes3mp.GetPreviousCellPosX(pid),
|
||||||
|
tes3mp.GetPreviousCellPosY(pid),
|
||||||
|
tes3mp.GetPreviousCellPosZ(pid))
|
||||||
|
end
|
||||||
|
|
||||||
|
function playerMap.isOutside(pid)
|
||||||
|
return playerMap.map[Players[pid]].isOutside
|
||||||
|
end
|
||||||
|
|
||||||
|
function playerMap.setPlayerPosition(pid, x, y, z)
|
||||||
|
local playerName = Players[pid].accoundName
|
||||||
|
|
||||||
|
playerMap.map[playerName].x = math.floor( 0.5 + x )
|
||||||
|
playerMap.map[playerName].y = math.floor( 0.5 + y )
|
||||||
|
playerMap.map[playerName].z = math.floor( 0.5 + z )
|
||||||
|
end
|
||||||
|
|
||||||
|
function playerMap.setPlayerRotation(pid, rotx, rotz)
|
||||||
|
local playerName = Players[pid].accoundName
|
||||||
|
playerMap.map[playerName].rotx = rotx
|
||||||
|
playerMap.map[playerName].rotz = rotz
|
||||||
|
|
||||||
|
local anglenorth = math.deg( rotz )
|
||||||
|
|
||||||
|
if anglenorth < 0 then
|
||||||
|
anglenorth = 360 + anglenorth
|
||||||
|
end
|
||||||
|
|
||||||
|
playerMap.map[playerName].rot = math.floor( 0.5 + anglenorth )
|
||||||
|
end
|
||||||
|
|
||||||
|
-- OnGUIAction is called when you, login or register (and for a bunch of other things besides)
|
||||||
|
customEventHooks.registerHandler("OnGUIAction", function(eventStatus, pid, idGui, data)
|
||||||
|
if (idGui ~= guiHelper.ID.LOGIN and idGui ~= guiHelper.ID.REGISTER) then
|
||||||
|
return nil -- We aren't interested in anything but these two events
|
||||||
|
end
|
||||||
|
|
||||||
|
if data == nil then
|
||||||
|
return nil -- data is nil when register or login fails
|
||||||
|
end
|
||||||
|
|
||||||
|
local player = Players[pid]
|
||||||
|
|
||||||
|
-- I don't really see how this could happen, but best to be safe.
|
||||||
|
if player == nil then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
playerMap.map[player.accountName] = {}
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- We want to use a validator here so that the player info still exists when this function is called.
|
||||||
|
-- If we registered a handler, it would be thrown away and all we would know is the pid, which
|
||||||
|
-- means we would not be able to throw away the player's account name key.
|
||||||
|
customEventHooks.registerValidator("OnPlayerDisconnect", function(eventStatus, pid)
|
||||||
|
if Players[pid] == nil or not Players[pid]:IsLoggedIn() then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
playerMap.map[Players[pid].accountName] = nil
|
||||||
|
return nil -- original eventstatus will be used in this case, we're not doing anything amazing
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- When the server starts, we want to start monitoring the players for
|
||||||
|
customEventHooks.registerHandler("OnServerPostInit", function(eventStatus)
|
||||||
|
playerMap.timer = tes3mp.CreateTimer("OnPlayerMapUpdateTick", updateInterval)
|
||||||
|
tes3mp.StartTimer(playerMap.timer)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- When the server exists we clear the json file of any entries so the map doesn't
|
||||||
|
-- "freeze" in a state of having some people on it.
|
||||||
|
customEventHooks.registerHandler("OnServerExit", function(eventStatus)
|
||||||
|
playerMap.map = {}
|
||||||
|
playerMap.save()
|
||||||
|
|
||||||
|
-- TODO: Stop the update timer. I suppose it will also get destroyed when
|
||||||
|
-- we exit so maybe this is fine too.
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- When the player changes from an exterior to an interior cell, we want to make sure that the last
|
||||||
|
-- saved coordinates are the ones the player had right before changing cells. This is the door.
|
||||||
|
customEventHooks.registerHandler("OnPlayerCellChange", function(eventStatus, pid)
|
||||||
|
playerMap.updatePlayer(pid)
|
||||||
|
return nil
|
||||||
|
end)
|
||||||
|
|
||||||
|
return playerMap
|
Loading…
Reference in New Issue