-
Notifications
You must be signed in to change notification settings - Fork 18
/
PlayerState.lua
214 lines (135 loc) · 4.72 KB
/
PlayerState.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
-- PlayerState.lua
-- Implements the cPlayerState object, representing the full information that is remembered per player
-- Also implements the GetPlayerState() function for retrieving / initializing the player state
--- The dict-table of player states.
--[[
Each player has an entry in this dictionary, indexed by the player's Key.
The player name has been chosen as the Key, this means that multiple players of the same name
share their state and the state is global for all worlds.
Each entry is a cPlayerState class instance
--]]
local g_PlayerStates = {}
--- Class for storing the player's state
local cPlayerState = {}
--- Creates a new PlayerState object
function cPlayerState:new(a_Obj, a_PlayerKey, a_Player)
assert(a_PlayerKey ~= nil)
assert(a_Player ~= nil)
a_Obj = a_Obj or {}
setmetatable(a_Obj, cPlayerState)
self.__index = self
-- Initialize the object members to their defaults:
local ClientHandle = a_Player:GetClientHandle()
if (ClientHandle ~= nil) then
a_Obj.IsWECUIActivated = ClientHandle:HasPluginChannel("WECUI")
end
a_Obj.Clipboard = cClipboard:new()
a_Obj.ClipboardStorage = cClipboardStorage:new({}, a_Obj.Clipboard)
a_Obj.PlayerKey = a_PlayerKey
a_Obj.Selection = cPlayerSelection:new({}, a_Obj)
a_Obj.UndoStack = cUndoStack:new({}, 10, a_Obj) -- TODO: Settable Undo depth (2nd param)
a_Obj.WandActivated = true
a_Obj.ToolRegistrator = cToolRegistrator:new({})
a_Obj.CraftScript = cCraftScript:new({})
return a_Obj
end
--- Calls the specified callback with the cPlayer instance of the player to whom this state belongs
-- Returns true if the callback has been called, false otherwise
function cPlayerState:DoWithPlayer(a_Callback)
local HasCalled = false
cRoot:Get():ForEachPlayer(
function(a_Player)
if (a_Player:GetName() == self.PlayerKey) then
HasCalled = true
a_Callback(a_Player)
return true
end
end
)
return HasCalled
end
function cPlayerState:GetUUID()
local UUID = nil
self:DoWithPlayer(
function(a_Player)
UUID = a_Player:GetUUID()
end
)
return UUID
end
--- Loads the state from persistent storage (if so configured)
function cPlayerState:Load()
local UUID = self:GetUUID()
if (g_Config.Storage.RememberPlayerSelection) then
self.Selection:Load(UUID)
end
end
--- Pushes one level of Undo onto the Undo stack, by cloning the BlockArea within the Selection
-- a_World is the cWorld where the selection is being copied, a_UndoName is a user-visible name that can be listed
function cPlayerState:PushUndoInSelection(a_World, a_UndoName)
-- Read the BlockArea:
local Area = cBlockArea()
local MinX, MaxX = self.Selection:GetXCoordsSorted()
local MinY, MaxY = self.Selection:GetYCoordsSorted()
local MinZ, MaxZ = self.Selection:GetZCoordsSorted()
Area:Read(a_World, MinX, MaxX, MinY, MaxY, MinZ, MaxZ)
-- Push the Undo onto the stack:
self.UndoStack:PushUndo(a_World, Area, a_UndoName)
end
--- Saves the state to persistent storage (if so configured)
function cPlayerState:Save(a_PlayerUUID)
if (g_Config.Storage.RememberPlayerSelection) then
self.Selection:Save(a_PlayerUUID)
end
end
--- Returns a PlayerState object for the specified Player
-- Creates one if it doesn't exist yet
function GetPlayerState(a_Player)
assert(tolua.type(a_Player) == "cPlayer")
local Key = a_Player:GetName()
local res = g_PlayerStates[Key]
if (res ~= nil) then
return res
end
-- The player state doesn't exist yet, create a new one:
res = cPlayerState:new({}, Key, a_Player)
g_PlayerStates[Key] = res
res:Load()
return res
end
local function OnPlayerDestroyed(a_Player)
-- Allow the player state to be saved to a persistent storage:
local State = g_PlayerStates[a_Player:GetName()]
if (State == nil) then
return false
end
State:Save(a_Player:GetUUID())
-- Remove the player state altogether:
g_PlayerStates[a_Player:GetName()] = nil
end
function ForEachPlayerState(a_Callback)
assert(type(a_Callback) == "function")
for _, State in pairs(g_PlayerStates) do
if (a_Callback(State)) then
break
end
end
end
local function OnPluginMessage(a_Client, a_Channel, a_Message)
if (a_Channel ~= "REGISTER") then
return
end
-- The client has registered for some channels, if they did register for WECUI, send the selection to them:
-- Find the cPlayer object for this client, if available:
local Player = a_Client:GetPlayer()
if (Player == nil) then
return
end
-- Send the selection (if there is one):
local State = GetPlayerState(Player)
State.IsWECUIActivated = a_Client:HasPluginChannel("WECUI")
State.Selection:NotifySelectionChanged()
end
-- Register the hooks needed:
cPluginManager:AddHook(cPluginManager.HOOK_PLAYER_DESTROYED, OnPlayerDestroyed)
cPluginManager:AddHook(cPluginManager.HOOK_PLUGIN_MESSAGE, OnPluginMessage)