Compare commits

...

10 Commits

5 changed files with 52 additions and 31 deletions

1
.gitignore vendored
View File

@ -1 +1,2 @@
*.bak
*.tmp

View File

@ -2,14 +2,11 @@ local debug = false
local CMH = {}
local datacache = {}
local delim = {"", "", "", "", ""}
local pck = {REQ = 1, DAT = 2}
local delim = {"", "", "", "", ""}
local pck = {REQ = "", DAT = ""}
-- HELPERS START
local function debugOut(prefix, x, msg)
prefix = prefix or ""
msg = msg or ""
x = x or ""
if(debug == true) then
print("["..date("%X", time()).."][CSMH]["..x.."]["..prefix.."]: "..msg)
end
@ -52,7 +49,12 @@ local function ParseMessage(str)
-- Convert value to correct type
for k, v in pairs(valTemp) do
local varType = typeTemp[k]
if(varType == 3) then -- Ints
if(varType == 2) then -- Strings
-- Special case for empty string parsing
if(v == string.char(tonumber('1A', 16))) then
v = ""
end
elseif(varType == 3) then -- Ints
v = tonumber(v)
elseif(varType == 4) then -- Tables
v = Smallfolk.loads(v, #v)
@ -75,6 +77,10 @@ local function ProcessVariables(reqId, ...)
for _, v in pairs(arg) do
if(type(v) == "string") then
-- Special case for empty string parsing
if(#v == 0) then
v = string.char(tonumber('1A', 16))
end
msg = msg .. delim[2]
elseif(type(v) == "number") then
msg = msg .. delim[3]
@ -109,16 +115,13 @@ function CMH.OnReceive(self, event, header, data, Type, sender)
-- Ensure the sender and receiver is the same, the message is an addon message, and the message type is WHISPER
if event == "CHAT_MSG_ADDON" and sender == UnitName("player") and Type == "WHISPER" then
-- unpack and validate addon message structure
local pfx, source, pckId = header:match("(...)(%u)(%d%d)")
local pfx, source, pckId = header:match("(.)(%u)(.)")
if not pfx or not source or not pckId then
return
end
-- Make sure we're only processing addon messages using our framework prefix character as well as client messages
if(pfx == delim[1] and source == "S") then
-- convert ID to number so we can compare with our packet list
pckId = tonumber(pckId)
if(pckId == pck.REQ) then
debugOut("REQ", "Rx", "REQ received, data: "..data)
CMH.OnREQ(sender, data)
@ -242,7 +245,7 @@ end
-- Tx START
function CMH.SendREQ(functionId, linkCount, reqId, addon)
local header = string.format("%01s%01s%02d", delim[1], "C", pck.REQ)
local header = string.format("%01s%01s%01s", delim[1], "C", pck.REQ)
local data = string.format("%02d%03d%06s%0"..tostring(#addon).."s", functionId, linkCount, reqId, addon)
SendAddonMessage(header, data, "WHISPER", UnitName("player"))
debugOut("REQ", "Tx", "Sent REQ with ID "..reqId..", sending DAT..")
@ -250,7 +253,7 @@ end
function CMH.SendDAT(reqId)
-- Build data message header
local header = string.format("%01s%01s%02d", delim[1], "C", pck.DAT)
local header = string.format("%01s%01s%01s", delim[1], "C", pck.DAT)
-- iterate all items in the message data cache and send
-- functions can also be trigger functions without any data, only send header and no payload

View File

@ -38,6 +38,15 @@ function StatPointUI.OnLogin(event, player)
StatPointUI.SetStats(player:GetGUIDLow())
end
function StatPointUI.AddStatPoint(guid)
local player = GetPlayerByGUID(guid)
if(player) then
StatPointUI.cache[guid][6] = StatPointUI.cache[guid][6]+1
CharDBQuery("UPDATE character_stats_extra SET `points`=`points`+1 WHERE `guid`="..guid..";")
player:SendServerResponse(config.Prefix, 1, StatPointUI.cache[guid])
end
end
function StatPointUI.SetStats(guid, stat)
stat = stat or nil
local player = GetPlayerByGUID(guid)
@ -121,6 +130,11 @@ function OnStatResetRequest(player, argTable)
player:SendServerResponse(config.Prefix, 1, StatPointUI.cache[player:GetGUIDLow()])
end
-- Helper function to add a stat point to the player through other scripts
function Player:AddPoint()
StatPointUI.AddStatPoint(self:GetGUIDLow())
end
RegisterPlayerEvent(3, StatPointUI.OnLogin)
RegisterServerEvent(33, StatPointUI.OnElunaStartup)
RegisterClientRequests(config)

View File

@ -2,14 +2,14 @@
# CSMH
### What is CSMH?
CSMH is a Client and Server Message Handler framework for communication between Eluna and the WoW interface. It has been tested on Mangos and TrinityCore 3.3.5a, but it will probably work for other versions as well.
CSMH is a **C**lient and **S**erver **M**essage **H**andler framework for communication between Eluna and the WoW client interface. It has been tested on Mangos and TrinityCore 3.3.5a, but it will probably work for other versions as well.
CSMH consists of two parts, the Client Message Handler and the Server Message Handler respectively.
### How does this compare to AIO?
While AIO is the most used solution of this kind, it has its drawbacks as well. While it allows you to write all your code server-side, it also limits you to the Lua API only. AIO also sends the full addon code to the client on startup and reload, which is fairly network intensive. Upside of AIO is its ease of distribution compared to dedicated client-side addons.
While AIO is the most used solution of this kind, it has its drawbacks as well. While it allows you to write all your code server-side, it also limits you to the Lua API only. AIO also sends the full addon code to the client on startup and reload, which is relatively bandwidth intensive compared to communication only. Upside of AIO is its ease of distribution compared to dedicated client-side addons.
CSMH is only meant to transport data between the client and the server, and is therefore not as network intensive as AIO. You write your server-side code on the server, and you distribute your client-side code either as an addon, or in a patch. This allows you to use XML and templates, as well as the full Lua API.
CSMH is only meant to transport data between the client and the server, and is therefore not as bandwidth intensive as AIO. You write your server-side code on the server, and you distribute your client-side code either as an addon, or in a patch. This allows you to use XML and templates, as well as the full Lua API.
Both AIO and CSMH uses smallfolk for serialization, and is compatible with each other. You can use both AIO and CSMH in the same project.
@ -45,14 +45,14 @@ The CMH can be distributed either as a stand-alone addon, or through a patch. Fi
- **RegisterServerResponses** takes a config table (see Both section for structure) and registers functionId's to corresponding client side functions. You then use these functionId's in SendServerResponse.
`SendClientRequest(prefix, functionId, ...)`
- **SendClientRequest** sends a Client Request to the Server, with *prefix* and *functionId* defined in your server config table. Takes any amount of string, integer, table or boolean variables and sends to the corresponding function.
- **SendClientRequest** sends a Client Request to the Server, with *prefix* and *functionId* defined in your server config table. Takes any amount of string, integer, table or boolean variables and sends to the corresponding function. Nil variables are not supported. This is default Lua behavior.
### Server:
`RegisterClientRequests(config table)`
- **RegisterClientRequests** takes a config table (see Both section for structure) and registers functionId's to corresponding server side functions. You then use these functionId's in SendClientRequest.
`Player:SendServerResponse(prefix, functionId, ...)`
- **SendServerResponse** sends a Server Response to the Client, with *prefix* and *functionId* defined in your client config table. Takes any amount of string, integer, table or boolean variables and sends to the corresponding function. Requires *player* object.
- **SendServerResponse** sends a Server Response to the Client, with *prefix* and *functionId* defined in your client config table. Takes any amount of string, integer, table or boolean variables and sends to the corresponding function. Nil variables are not supported. This is default Lua behavior. Requires *player* object.
### Both:
local config = {
@ -68,7 +68,7 @@ The CMH can be distributed either as a stand-alone addon, or through a patch. Fi
## Credits:
- [Stoneharry](https://github.com/stoneharry)
- [Terrorblade](https://github.com/Terrorblade)
- Kaev
- [Kaev](https://github.com/kaev)
- [Rochet / AIO](https://github.com/Rochet2)
- [Eluna](https://github.com/ElunaLuaEngine/Eluna)
- [smallfolk](https://github.com/gvx/Smallfolk)

View File

@ -4,14 +4,11 @@ local debug = false
local SMH = {}
local datacache = {}
local delim = {"", "", "", "", ""}
local pck = {REQ = 1, DAT = 2}
local delim = {"", "", "", "", ""}
local pck = {REQ = "", DAT = ""}
-- HELPERS START
local function debugOut(prefix, x, msg)
prefix = prefix or ""
msg = msg or ""
x = x or ""
if(debug == true) then
print("["..os.clock().."][CSMH]["..x.."]["..prefix.."]: "..msg)
end
@ -54,7 +51,12 @@ local function ParseMessage(str)
-- Convert value to correct type
for k, v in pairs(valTemp) do
local varType = typeTemp[k]
if(varType == 3) then -- Ints
if(varType == 2) then -- strings
-- special case for empty string parsing
if(v == string.char(tonumber('1A', 16))) then
v = ""
end
elseif(varType == 3) then -- Ints
v = tonumber(v)
elseif(varType == 4) then -- Tables
v = smallfolk.loads(v)
@ -77,6 +79,10 @@ local function ProcessVariables(sender, reqId, ...)
for _, v in pairs(arg) do
if(type(v) == "string") then
-- Special case for empty string parsing
if(#v == 0) then
v = string.char(tonumber('1A', 16))
end
msg = msg .. delim[2]
elseif(type(v) == "number") then
msg = msg .. delim[3]
@ -119,16 +125,13 @@ function SMH.OnReceive(event, sender, _type, header, data, target)
-- Ensure the sender and receiver is the same, and the message type is WHISPER
if sender:GetName() == target:GetName() and _type == 7 then
-- unpack and validate addon message structure
local pfx, source, pckId = header:match("(...)(%u)(%d%d)")
local pfx, source, pckId = header:match("(.)(%u)(.)")
if not pfx or not source or not pckId then
return
end
-- Make sure we're only processing addon messages using our framework prefix character as well as client messages
if(pfx == delim[1] and source == "C") then
-- convert ID to number so we can compare with our packet list
pckId = tonumber(pckId)
if(pckId == pck.REQ) then
debugOut("REQ", "Rx", "REQ received, data: "..data)
SMH.OnREQ(sender, data)
@ -254,7 +257,7 @@ end
-- Tx START
function SMH.SendREQ(sender, functionId, linkCount, reqId, addon)
local header = string.format("%01s%01s%02d", delim[1], "S", pck.REQ)
local header = string.format("%01s%01s%01s", delim[1], "S", pck.REQ)
local data = string.format("%02d%03d%06s%0"..tostring(#addon).."s", functionId, linkCount, reqId, addon)
sender:SendAddonMessage(header, data, 7, sender)
debugOut("REQ", "Tx", "Sent REQ with ID "..reqId..", sending DAT..")
@ -262,7 +265,7 @@ end
function SMH.SendDAT(sender, reqId)
-- Build data message header
local header = string.format("%01s%01s%02d", delim[1], "S", pck.DAT)
local header = string.format("%01s%01s%01s", delim[1], "S", pck.DAT)
-- iterate all items in the message data cache and send
-- functions can also be trigger functions without any data, only send header and no payload