212 lines
7.1 KiB
Lua
212 lines
7.1 KiB
Lua
package.path = package.path .. ";lua/?.lua"
|
|
|
|
local date = require('date')
|
|
|
|
function readAccountBridge()
|
|
local json = require("json")
|
|
local io = require("io")
|
|
|
|
-- TODO: Custom path.
|
|
f, err = io.open("databases/accounts.json", "r")
|
|
if err then
|
|
print("ToontownClient: Returning empty table for account bridge")
|
|
return {}
|
|
end
|
|
|
|
decoder = json.new_decoder(f)
|
|
result, err = decoder:decode()
|
|
f:close()
|
|
assert(not err, err)
|
|
print("ToontownClient: Account bridge successfully loaded.")
|
|
return result
|
|
end
|
|
|
|
ACCOUNT_BRIDGE = readAccountBridge()
|
|
|
|
function saveAccountBridge()
|
|
local json = require("json")
|
|
local io = require("io")
|
|
|
|
-- TODO: Custom path.
|
|
f, err = io.open("databases/accounts.json", "w")
|
|
assert(not err, err)
|
|
encoder = json.new_encoder(f)
|
|
err = encoder:encode(ACCOUNT_BRIDGE)
|
|
assert(not err, err)
|
|
end
|
|
|
|
-- Load message types
|
|
dofile("lua/MsgTypes.lua")
|
|
|
|
function receiveDatagram(client, dgi)
|
|
-- Client received datagrams
|
|
msgType = dgi:readUint16()
|
|
|
|
if msgType == CLIENT_HEARTBEAT then
|
|
client:handleHeartbeat()
|
|
elseif msgType == CLIENT_DISCONNECT then
|
|
client:handleDisconnect()
|
|
elseif msgType == CLIENT_LOGIN_TOONTOWN then
|
|
handleLoginToontown(client, dgi)
|
|
-- We have reached the only message types unauthenticated clients can use.
|
|
elseif not client:authenticated() then
|
|
client:sendDisconnect(CLIENT_DISCONNECT_GENERIC, "First datagram is not CLIENT_LOGIN_TOONTOWN", true)
|
|
else
|
|
client:sendDisconnect(CLIENT_DISCONNECT_GENERIC, string.format("Unknown message type: %d", msgType), true)
|
|
end
|
|
|
|
if dgi:getRemainingSize() ~= 0 then
|
|
client:sendDisconnect(CLIENT_DISCONNECT_OVERSIZED_DATAGRAM, string.format("Datagram contains excess data.\n%s", tostring(dgi)), true)
|
|
end
|
|
end
|
|
|
|
function handleLoginToontown(client, dgi)
|
|
local playToken = dgi:readString()
|
|
local version = dgi:readString()
|
|
local hash = dgi:readUint32()
|
|
local tokenType = dgi:readInt32()
|
|
local wantMagicWords = dgi:readString()
|
|
|
|
if client:authenticated() then
|
|
client:sendDisconnect(CLIENT_DISCONNECT_RELOGIN, "Authenticated client tried to login twice!", true)
|
|
return
|
|
end
|
|
|
|
-- Check if version and hash matches
|
|
if version ~= SERVER_VERSION then
|
|
client:sendDisconnect(CLIENT_DISCONNECT_BAD_VERSION, string.format("Client version mismatch: client=%s, server=%s", version, SERVER_VERSION), true)
|
|
return
|
|
end
|
|
|
|
if hash ~= DC_HASH then
|
|
client:sendDisconnect(CLIENT_DISCONNECT_BAD_VERSION, string.format("Client DC hash mismatch: client=%d, server=%d", hash, DC_HASH), true)
|
|
return
|
|
end
|
|
|
|
-- TODO: Make these configurable.
|
|
local speedChatPlus = true
|
|
local openChat = true
|
|
local isPaid = true
|
|
local dislId = 1
|
|
local linkedToParent = false
|
|
|
|
local accountId = ACCOUNT_BRIDGE[playToken]
|
|
if accountId ~= nil then
|
|
-- Query the account object
|
|
client:getDatabaseValues(accountId, "Account", {"ACCOUNT_AV_SET", "CREATED", "LAST_LOGIN"}, function (doId, success, fields)
|
|
if not success then
|
|
client:sendDisconnect(CLIENT_DISCONNECT_ACCOUNT_ERROR, "The Account object was unable to be queried.", true)
|
|
return
|
|
end
|
|
|
|
-- Update LAST_LOGIN
|
|
fields.LAST_LOGIN = os.date("%a %b %d %H:%M:%S %Y")
|
|
client:setDatabaseValues(accountId, "Account", {
|
|
LAST_LOGIN = fields.LAST_LOGIN,
|
|
})
|
|
|
|
loginAccount(client, fields, accountId, playToken, openChat, isPaid, dislId, linkedToParent, speedChatPlus)
|
|
end)
|
|
else
|
|
-- Create a new account object
|
|
local account = {
|
|
-- The rest of the values are defined in the dc file.
|
|
CREATED = os.date("%a %b %d %H:%M:%S %Y"),
|
|
LAST_LOGIN = os.date("%a %b %d %H:%M:%S %Y"),
|
|
}
|
|
|
|
client:createDatabaseObject("Account", account, DATABASE_OBJECT_TYPE_ACCOUNT, function (accountId)
|
|
if accountId == 0 then
|
|
client:sendDisconnect(CLIENT_DISCONNECT_ACCOUNT_ERROR, "The Account object was unable to be created.", false)
|
|
return
|
|
end
|
|
|
|
-- Store the account into the bridge
|
|
ACCOUNT_BRIDGE[playToken] = accountId
|
|
saveAccountBridge()
|
|
|
|
account.ACCOUNT_AV_SET = {0, 0, 0, 0, 0, 0}
|
|
|
|
client:writeServerEvent("account-created", "ToontownClient", string.format("%d", accountId))
|
|
|
|
loginAccount(client, account, accountId, playToken, openChat, isPaid, dislId, linkedToParent, speedChatPlus)
|
|
end)
|
|
end
|
|
end
|
|
|
|
function loginAccount(client, account, accountId, playToken, openChat, isPaid, dislId, linkedToParent, speedChatPlus)
|
|
-- Eject other client if already logged in.
|
|
local ejectDg = datagram:new()
|
|
client:addServerHeaderWithAccountId(ejectDg, accountId, CLIENTAGENT_EJECT)
|
|
ejectDg:addUint16(100)
|
|
ejectDg:addString("You have been disconnected because someone else just logged in using your account on another computer.")
|
|
client:routeDatagram(ejectDg)
|
|
|
|
-- Subscribe to our puppet channel.
|
|
client:subscribePuppetChannel(accountId, 3)
|
|
|
|
-- Set our channel containing our account id
|
|
client:setChannel(accountId, 0)
|
|
|
|
client:authenticated(true)
|
|
|
|
-- Store the account id and avatar list into our client's user table:
|
|
local userTable = client:userTable()
|
|
userTable.accountId = accountId
|
|
userTable.avatars = account.ACCOUNT_AV_SET
|
|
userTable.playToken = playToken
|
|
userTable.isPaid = isPaid
|
|
userTable.speedChatPlus = speedChatPlus
|
|
userTable.openChat = openChat
|
|
client:userTable(userTable)
|
|
|
|
-- Log the event
|
|
client:writeServerEvent("account-login", "ToontownClient", string.format("%d", accountId))
|
|
|
|
-- Prepare the login response.
|
|
local resp = datagram:new()
|
|
resp:addUint16(CLIENT_LOGIN_TOONTOWN_RESP)
|
|
resp:addUint8(0) -- Return code
|
|
resp:addString("All Ok")
|
|
resp:addUint32(dislId) -- accountNumber
|
|
resp:addString(playToken) -- accountName
|
|
resp:addUint8(1) -- accountNameApproved
|
|
|
|
if openChat then
|
|
resp:addString('YES') -- openChatEnabled, does not seem to be used
|
|
else
|
|
resp:addString('NO') -- openChatEnabled, does not seem to be used
|
|
end
|
|
|
|
resp:addString('YES') -- createFriendsWithChat
|
|
resp:addString('YES') -- chatCodeCreationRule
|
|
resp:addUint32(os.time()) -- sec
|
|
resp:addUint32(os.clock()) -- usec
|
|
|
|
if isPaid then
|
|
resp:addString("FULL") -- access
|
|
else
|
|
resp:addString("VELVET") -- access
|
|
end
|
|
|
|
if speedChatPlus then
|
|
resp:addString("YES") -- WhiteListResponse
|
|
else
|
|
resp:addString("NO") -- WhiteListResponse
|
|
end
|
|
|
|
resp:addString(os.date("%Y-%m-%d %H:%M:%S")) -- lastLoggedInStr
|
|
resp:addInt32(math.floor(date.diff(account.LAST_LOGIN, account.CREATED):spandays())) -- accountDays
|
|
|
|
if linkedToParent then
|
|
resp:addString("WITH_PARENT_ACCOUNT") -- toonAccountType
|
|
else
|
|
resp:addString("NO_PARENT_ACCOUNT") -- toonAccountType
|
|
end
|
|
|
|
resp:addString(playToken) -- userName
|
|
|
|
-- Dispatch the response to the client.
|
|
client:sendDatagram(resp)
|
|
end
|