Skip to content

Commit d625571

Browse files
committed
0 parents  commit d625571

7 files changed

+490
-0
lines changed

api/messageReceiverSynced.lua

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
local moduleInfo = {
2+
name = "customCommandsMessageReceiverSynced",
3+
desc = "API for receiving messages triggering synced code.",
4+
author = "PepeAmpere",
5+
date = "2017-02-13",
6+
license = "notAlicense",
7+
}
8+
9+
local newReceiveCustomMessage = {
10+
["RegisterCustomCommand"] = function(decodedMsg, playerID, context)
11+
if (decodedMsg ~= nil and decodedMsg.name ~= nil) then
12+
customCommands.RegisterCustomCommand(decodedMsg, playerID)
13+
else
14+
Spring.Echo("[customCommandsMessageReceiverSynced] Command [" .. decodedMsg.name .. "] was not registered due invalid input")
15+
end
16+
end,
17+
18+
["DeregisterCustomCommand"] = function(decodedMsg, playerID, context)
19+
if (decodedMsg ~= nil and decodedMsg.name ~= nil) then
20+
-- we currently have no way how to trully deregister given command, we just remove its instances from all units
21+
customCommands.RemoveCustomCommandFromAllUnits(decodedMsg.name, playerID)
22+
else
23+
Spring.Echo("[customCommandsMessageReceiverSynced] Command [" .. decodedMsg.name .. "] was not deregistered due invalid input")
24+
end
25+
end,
26+
}
27+
28+
-- END OF MODULE DEFINITIONS --
29+
30+
-- update global structures
31+
message.AttachCustomReceiver(newReceiveCustomMessage, moduleInfo)

api/messageReceiverUnsynced.lua

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
local moduleInfo = {
2+
name = "customCommandsMessageReceiverUnsynced",
3+
desc = "Internal API for triggerring unsynced code.",
4+
author = "PepeAmpere",
5+
date = "2017-02-19",
6+
license = "notAlicense",
7+
}
8+
9+
local newReceiveCustomMessage = {
10+
["CustomCommandUpdate"] = function(_, cmdID)
11+
if (Script.LuaUI('CustomCommandUpdate')) then
12+
Script.LuaUI.CustomCommandUpdate(cmdID)
13+
end
14+
end,
15+
16+
["CustomCommandRegistered"] = function(_, encodedMessage)
17+
local decodedMsg = message.Decode(encodedMessage)
18+
if (Script.LuaUI('CustomCommandRegistered')) then
19+
Script.LuaUI.CustomCommandRegistered(decodedMsg.name, decodedMsg.id)
20+
end
21+
end,
22+
}
23+
24+
-- END OF MODULE DEFINITIONS --
25+
26+
-- update global structures
27+
message.AttachCustomReceiver(newReceiveCustomMessage, moduleInfo)

api/messageSenderExternalWidget.lua

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
local moduleInfo = {
2+
name = "customCommandsMessageSenderExternalWidget",
3+
desc = "API helper for external widgets using the module",
4+
author = "PepeAmpere",
5+
date = "2017-02-13",
6+
license = "notAlicense",
7+
}
8+
9+
-- @description public API for anyone who wants to inject or edit custom commands via own widget
10+
local newSendCustomMessage = {
11+
["RegisterCustomCommand"] = function(commandDescription)
12+
13+
-- mandatory
14+
if (commandDescription == nil or type(commandDescription) ~= "table") then Spring.Echo("[" .. moduleInfo.name .. "]" .. "WARNING: Attempt to send message [RegisterCustomCommand] with wrong parameter for [commandDescription]") end
15+
if (commandDescription.type == nil or type(commandDescription.type) ~= "number") then Spring.Echo("[" .. moduleInfo.name .. "]" .. "WARNING: Attempt to send message [RegisterCustomCommand] with wrong parameter for [commandDescription.type]") end
16+
if (commandDescription.name == nil or type(commandDescription.name ) ~= "string") then Spring.Echo("[" .. moduleInfo.name .. "]" .. "WARNING: Attempt to send message [RegisterCustomCommand] with wrong parameter for [commandDescription.name]") end
17+
if (commandDescription.cursor == nil or type(commandDescription.cursor) ~= "string") then Spring.Echo("[" .. moduleInfo.name .. "]" .. "WARNING: Attempt to send message [RegisterCustomCommand] with wrong parameter for [commandDescription.cursor]") end
18+
if (commandDescription.action == nil or type(commandDescription.action) ~= "string") then Spring.Echo("[" .. moduleInfo.name .. "]" .. "WARNING: Attempt to send message [RegisterCustomCommand] with wrong parameter for [commandDescription.action]") end
19+
if (commandDescription.tooltip == nil or type(commandDescription.tooltip) ~= "string") then Spring.Echo("[" .. moduleInfo.name .. "]" .. "WARNING: Attempt to send message [RegisterCustomCommand] with wrong parameter for [commandDescription.tooltip]") end
20+
if (commandDescription.hidden == nil or type(commandDescription.hidden) ~= "boolean") then Spring.Echo("[" .. moduleInfo.name .. "]" .. "WARNING: Attempt to send message [RegisterCustomCommand] with wrong parameter for [commandDescription.hidden]") end
21+
22+
-- NOTA UI mandatory
23+
if (commandDescription.UIoverride == nil or type(commandDescription.UIoverride) ~= "table") then Spring.Echo("[" .. moduleInfo.name .. "]" .. "WARNING: Attempt to send message [RegisterCustomCommand] with wrong parameter for [commandDescription.UIoverride]") end
24+
25+
-- optional for Spring
26+
if (commandDescription.texture ~= nil and type(commandDescription.texture) ~= "string") then Spring.Echo("[" .. moduleInfo.name .. "]" .. "WARNING: Attempt to send message [RegisterCustomCommand] with wrong parameter for [commandDescription.texture]") end
27+
if (commandDescription.queueing ~= nil and type(commandDescription.queueing) ~= "boolean") then Spring.Echo("[" .. moduleInfo.name .. "]" .. "WARNING: Attempt to send message [RegisterCustomCommand] with wrong parameter for [commandDescription.queueing]") end
28+
if (commandDescription.disabled ~= nil and type(commandDescription.disabled) ~= "boolean") then Spring.Echo("[" .. moduleInfo.name .. "]" .. "WARNING: Attempt to send message [RegisterCustomCommand] with wrong parameter for [commandDescription.disabled]") end
29+
if (commandDescription.showUnique ~= nil and type(commandDescription.showUnique) ~= "boolean") then Spring.Echo("[" .. moduleInfo.name .. "]" .. "WARNING: Attempt to send message [RegisterCustomCommand] with wrong parameter for [commandDescription.showUnique]") end
30+
if (commandDescription.onlyTexture ~= nil and type(commandDescription.onlyTexture) ~= "boolean") then Spring.Echo("[" .. moduleInfo.name .. "]" .. "WARNING: Attempt to send message [RegisterCustomCommand] with wrong parameter for [commandDescription.onlyTexture]") end
31+
if (commandDescription.params ~= nil and type(commandDescription.params) ~= "table") then Spring.Echo("[" .. moduleInfo.name .. "]" .. "WARNING: Attempt to send message [RegisterCustomCommand] with wrong parameter for [commandDescription.params]") end
32+
33+
-- optional
34+
if (commandDescription.whitelist ~= nil and type(commandDescription.whitelist) ~= "table") then Spring.Echo("[" .. moduleInfo.name .. "]" .. "WARNING: Attempt to send message [registerCustomCommand] with wrong parameter for [commandDescription.whitelist]") end
35+
36+
commandDescription["subject"] = "RegisterCustomCommand"
37+
38+
message.SendRules(commandDescription)
39+
end,
40+
41+
["DeregisterCustomCommand"] = function(commandName)
42+
43+
if (commandName == nil or type(commandName) ~= "string") then Spring.Echo("[" .. moduleInfo.name .. "]" .. "WARNING: Attempt to send message [DeregisterCustomCommand] with wrong parameter for [commandName]") end
44+
45+
local newMessage = {
46+
subject = "DeregisterCustomCommand",
47+
name = commandName,
48+
}
49+
message.SendRules(newMessage)
50+
end,
51+
}
52+
53+
-- END OF MODULE DEFINITIONS --
54+
55+
-- update global structures
56+
message.AttachCustomSender(newSendCustomMessage, moduleInfo)

api/messageSenderInternal.lua

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
local moduleInfo = {
2+
name = "customCommandsMessageSenderInternal",
3+
desc = "Internal API",
4+
author = "PepeAmpere",
5+
date = "2017-02-19",
6+
license = "notAlicense",
7+
}
8+
9+
local newSendCustomMessage = {
10+
["CustomCommandUpdate"] = function(cmdID)
11+
-- mandatory
12+
if (cmdID == nil or type(cmdID) ~= "number") then Spring.Echo("[" .. moduleInfo.name .. "]" .. "WARNING: Attempt to send message [CustomCommandUpdate] with wrong parameter for [cmdID]") end
13+
14+
message.SendSyncedToUnsyncedDecoded("CustomCommandUpdate", cmdID)
15+
end,
16+
17+
["CustomCommandRegistered"] = function(cmdName, cmdID)
18+
-- mandatory
19+
if (cmdName == nil or type(cmdName) ~= "string") then Spring.Echo("[" .. moduleInfo.name .. "]" .. "WARNING: Attempt to send message [CustomCommandRegistered] with wrong parameter for [cmdName]") end
20+
if (cmdID == nil or type(cmdID) ~= "number") then Spring.Echo("[" .. moduleInfo.name .. "]" .. "WARNING: Attempt to send message [CustomCommandRegistered] with wrong parameter for [cmdID]") end
21+
22+
local newMessage = {
23+
subject = "CustomCommandRegistered",
24+
name = cmdName,
25+
id = cmdID,
26+
}
27+
28+
message.SendSyncedToUnsynced(newMessage)
29+
end,
30+
}
31+
32+
-- END OF MODULE DEFINITIONS --
33+
34+
-- update global structures
35+
message.AttachCustomSender(newSendCustomMessage, moduleInfo)

customCommands.lua

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
local moduleInfo = {
2+
name = "customCommands",
3+
desc = "customCommands core",
4+
author = "PepeAmpere",
5+
date = "2017/02/13",
6+
license = "notAlicense",
7+
}
8+
9+
-- moduel global structures
10+
cmdCounter = 36000
11+
customCommandsNameToID = {}
12+
customCommandsIDToOverride = {}
13+
customCommandsIDToName = {}
14+
customCommandsDescriptions = {}
15+
customCommandsNameToTeamID = {}
16+
customCommandsNameToPlayerID = {}
17+
customCommandsNameToWhitelist = {}
18+
customCommandsNameToName = {} -- internal name to requested name, populated only once
19+
customCommandsCountsPerTeam = {}
20+
21+
CUSTOM_COMMANDS_PER_TEAM_MAX = 100 + 1
22+
23+
customCommands = {
24+
25+
-- INTERNAL (PRIVATE) MODULE FUNCTIONS
26+
27+
-- @description Function which generates internal command key based on requested cmdName and playerID
28+
-- @privacy private to customCommands module
29+
-- @argument cmdName [string] cmdName as requested by player
30+
-- @argument playerID [number] Spring playerID
31+
-- @return internalCmdName [string] unique command name as key for all tables
32+
["CreateInternalCommandName"] = function(cmdName, playerID)
33+
return cmdName .. "_" .. playerID
34+
end,
35+
36+
-- @description Function which encodes filtered cmdName => cmdID mapping
37+
-- @privacy private to customCommands module
38+
-- @argument fullTable [table] table customCommandsNameToID
39+
-- @argument playerID [number] Spring playerID
40+
-- @return encodedTable [string] filtered mapping as string
41+
["EncodeLocalNameToID"] = function(fullTable, playerID)
42+
local thisPlayersList = {}
43+
for internalCmdName, cmdID in pairs(fullTable) do
44+
if (customCommandsNameToPlayerID[internalCmdName] == playerID) then
45+
thisPlayersList[customCommandsNameToName[internalCmdName]] = cmdID
46+
end
47+
end
48+
return message.Encode(thisPlayersList)
49+
end,
50+
51+
-- @description Function which encodes filtered cmdID => UIoverride table mapping
52+
-- @privacy private to customCommands module
53+
-- @argument fullTable [table] table customCommandsIDToOverride
54+
-- @argument playerID [number] Spring playerID
55+
-- @return encodedTable [string] filtered mapping as string
56+
["EncodeLocalOverride"] = function(fullTable, playerID)
57+
local thisPlayersList = {}
58+
for cmdID, UIoverride in pairs(fullTable) do
59+
if (customCommandsNameToPlayerID[customCommandsIDToName[cmdID]] == playerID) then
60+
thisPlayersList[cmdID] = UIoverride
61+
end
62+
end
63+
return message.Encode(thisPlayersList)
64+
end,
65+
66+
-- @description Function for registering or updating custom command
67+
-- @privacy private to customCommands module
68+
-- @argument decodedMsg [table] decoded table of whole registration request containing all mandatory cmdDesc data
69+
-- @argument playerID [number] Spring playerID
70+
["RegisterCustomCommand"] = function(decodedMsg, playerID)
71+
local cmdName = decodedMsg.name
72+
local internalCmdName = customCommands.CreateInternalCommandName(cmdName, playerID)
73+
customCommandsNameToName[internalCmdName] = cmdName
74+
local _,_,_,teamID = Spring.GetPlayerInfo(playerID)
75+
if (customCommandsCountsPerTeam[teamID] == nil) then customCommandsCountsPerTeam[teamID] = 0 end -- init custom commands counter if first request
76+
77+
-- 1) new registration allowed if
78+
-- * limit of 1000 commands was not reached
79+
-- * given command name was not registered yet
80+
-- * given team has registered less than CUSTOM_COMMANDS_PER_TEAM_MAX custom commands already
81+
-- 2) overwrite registration data for already used command name
82+
-- * given command was registered
83+
-- * if requesting playerID matches saved playerID for given command name
84+
if ((customCommandsNameToID[internalCmdName] == nil and cmdCounter < 37000 and customCommandsCountsPerTeam[teamID] < CUSTOM_COMMANDS_PER_TEAM_MAX) or
85+
(customCommandsNameToID[internalCmdName] ~= nil and customCommandsNameToPlayerID[internalCmdName] == playerID)) then
86+
local cmdID
87+
88+
if (customCommandsNameToID[internalCmdName] == nil) then -- new command
89+
cmdID = cmdCounter -- take prepared new ID
90+
cmdCounter = cmdCounter + 1 -- prepare new ID
91+
customCommandsNameToID[internalCmdName] = cmdID -- to prevent commands with duplicit names and readding same command multiple times
92+
customCommandsIDToName[cmdID] = internalCmdName -- reverse mapping
93+
gadgetHandler:RegisterCMDID(cmdID)
94+
95+
-- name to cmdID mapping
96+
local encodedCustomCommandsNameToID = customCommands.EncodeLocalNameToID(customCommandsNameToID, playerID)
97+
message.SendSyncedInfoTeamPacked("CustomCommandsNameToID", encodedCustomCommandsNameToID , teamID) -- save mapping to global variable so any widget can reload it from there
98+
99+
-- register owner player and owner team
100+
customCommandsNameToPlayerID[internalCmdName] = playerID
101+
customCommandsNameToTeamID[internalCmdName] = teamID
102+
103+
-- increase commands counter
104+
customCommandsCountsPerTeam[teamID] = customCommandsCountsPerTeam[teamID] + 1
105+
else -- already existing
106+
-- we remove all existing instance of old command to make sure it is not forgotten
107+
customCommands.RemoveCustomCommandFromAllUnits(cmdName, playerID)
108+
109+
cmdID = customCommandsNameToID[internalCmdName]
110+
end
111+
112+
customCommandsIDToOverride[cmdID] = decodedMsg.UIoverride
113+
114+
-- update UI data, send notifications
115+
local encodedCustomCommandsIDToOverride = customCommands.EncodeLocalOverride(customCommandsIDToOverride, playerID)
116+
message.SendSyncedInfoTeamPacked("CustomCommandsIDToOverride", encodedCustomCommandsIDToOverride, teamID) -- save override info to global variable so any widget can reload it from there
117+
sendCustomMessage.CustomCommandUpdate(cmdID) -- event like notification for any widget
118+
sendCustomMessage.CustomCommandRegistered(cmdName, cmdID) -- notify all widgets about registration (no matter if new or overriden)
119+
120+
-- construct valid commandDescription which does not contain invalid key=>value pairs
121+
local newCommandDescription = {
122+
id = cmdID,
123+
type = decodedMsg.type,
124+
name = cmdName,
125+
cursor = decodedMsg.cursor,
126+
action = decodedMsg.action,
127+
tooltip = decodedMsg.tooltip,
128+
hidden = decodedMsg.hidden,
129+
-- not used yet
130+
texture = decodedMsg.texture,
131+
queueing = decodedMsg.queueing,
132+
disabled = decodedMsg.disabled,
133+
showUnique = decodedMsg.showUnique,
134+
onlyTexture = decodedMsg.onlyTexture,
135+
params = decodedMsg.params,
136+
}
137+
138+
customCommandsDescriptions[internalCmdName] = newCommandDescription
139+
140+
-- construct whitelist
141+
local whitelistMap = {}
142+
local msgWhitelist = decodedMsg.whitelist
143+
if (msgWhitelist ~= nil) then
144+
for i=1, #msgWhitelist do
145+
whitelistMap[msgWhitelist[i]] = true
146+
end
147+
customCommandsNameToWhitelist[internalCmdName] = whitelistMap
148+
else -- clear it if updating command and there is no new whitelist
149+
customCommandsNameToWhitelist[internalCmdName] = nil
150+
end
151+
152+
for _, unitID in pairs(Spring.GetTeamUnits(teamID)) do
153+
customCommands.UnitIncomming(unitID, Spring.GetUnitDefID(unitID), teamID, nil)
154+
end
155+
else
156+
Spring.Echo("[customCommands] Command [" .. cmdName .. "] was not registered because 1) limit reached or 2) trying to register command with already used name and you are not its owner")
157+
end
158+
end,
159+
160+
-- FAKE DEREGISTRAION :)
161+
-- @description We just remove given command from all places where it was deployed
162+
-- @privacy private to customCommands module
163+
-- @argument internalCmdName [string] name of the command we want to deregister
164+
-- @argument playerID [number] Spring playerID
165+
-- @comment WARNING: the commandID is still occupied in engine!
166+
["RemoveCustomCommandFromAllUnits"] = function(cmdName, playerID)
167+
local _,_,_,teamID = Spring.GetPlayerInfo(playerID)
168+
local internalCmdName = customCommands.CreateInternalCommandName(cmdName, playerID)
169+
local cmdDesc = customCommandsDescriptions[internalCmdName]
170+
if (cmdDesc ~= nil and cmdDesc.id ~= nil and customCommandsNameToPlayerID[internalCmdName] == playerID) then -- only registrator can remove all instances of his own command from all units
171+
for _, unitID in pairs(Spring.GetTeamUnits(teamID)) do
172+
local index = Spring.FindUnitCmdDesc(unitID, cmdDesc.id)
173+
if (index ~= nil) then
174+
Spring.RemoveUnitCmdDesc(unitID, index)
175+
end
176+
end
177+
customCommandsDescriptions[internalCmdName] = nil -- registration and internalCmdName => cmdID mapping is kept, but cmdDescription is deleted
178+
end
179+
end,
180+
181+
-- PUBLIC MODULE FUNCTIONS
182+
183+
-- @description handler API used for getting unit by any means which (re)sets the system to proper state once called (by adding, removing or updating available commands)
184+
-- @argument unitID [number] Spring unitID
185+
-- @argument unitDefID [number] Spring unitDefID
186+
-- @argument teamID [number] Spring teamID
187+
-- @argument builderID [number] Spring unitID of builder of given unit
188+
["UnitIncomming"] = function(unitID, unitDefID, teamID, builderID)
189+
unitName = UnitDefs[unitDefID].name
190+
for internalCmdName, cmdDesc in pairs(customCommandsDescriptions) do
191+
if (customCommandsNameToTeamID[internalCmdName] == teamID) then -- if ownded by proper team
192+
local whitelist = customCommandsNameToWhitelist[internalCmdName]
193+
if (whitelist == nil or whitelist[unitName]) then -- if no whitelist (= all units) or unit on the whitelist
194+
local index = Spring.FindUnitCmdDesc(unitID, cmdDesc.id)
195+
if (index == nil) then -- if not added already
196+
Spring.InsertUnitCmdDesc(unitID, cmdDesc)
197+
else -- or update only
198+
Spring.EditUnitCmdDesc(unitID, index, cmdDesc)
199+
end
200+
end
201+
end
202+
end
203+
end,
204+
}

init.lua

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
-- universal load for customCommands module
2+
-- if you want customize it for your game, load your configs BEFORE including this file
3+
4+
local MODULE_NAME = "customCommands"
5+
Spring.Echo("-- " .. MODULE_NAME .. " LOADING --")
6+
7+
------------------------------------------------------
8+
9+
-- MANDATORY
10+
-- check required modules
11+
if (modules == nil) then Spring.Echo("[" .. MODULE_NAME .. "] ERROR: required madatory config [modules] listing paths for modules is missing") end
12+
if (attach == nil) then Spring.Echo("[" .. MODULE_NAME .. "] ERROR: required madatory library [attach] for loading files and modules is missing") end
13+
if (message == nil) then Spring.Echo("[" .. MODULE_NAME .. "] ERROR: required madatory module [message] for communication is missing") end
14+
15+
------------------------------------------------------
16+
17+
local thisModuleData = modules[MODULE_NAME]
18+
local THIS_MODULE_DATA_PATH = thisModuleData.data.path
19+
20+
-- LOAD INTERNAL MODULE FUNCTIONALITY
21+
if (widget) then
22+
attach.File(THIS_MODULE_DATA_PATH .. "api/messageSenderExternalWidget.lua") -- API helper for external widgets
23+
else
24+
if (gadgetHandler:IsSyncedCode()) then
25+
attach.File(THIS_MODULE_DATA_PATH .. "api/messageSenderInternal.lua") -- API helper for internal usage
26+
attach.File(THIS_MODULE_DATA_PATH .. "api/messageReceiverSynced.lua") -- hard API for processing inputs into synced code
27+
attach.File(THIS_MODULE_DATA_PATH .. "customCommands.lua") -- core customCommands library with main functionality of the module
28+
else
29+
attach.File(THIS_MODULE_DATA_PATH .. "api/messageReceiverUnsynced.lua") -- hard API for processing inputs into unsynced code
30+
end
31+
end
32+
------------------------------------------------------
33+
34+
Spring.Echo("-- " .. MODULE_NAME .. " LOADING FINISHED --")

0 commit comments

Comments
 (0)