RE:NODE

სახელმძღვანელოები14 წუთის საკითხავი

BeamMP-ის Lua plugin-ები: ბრძანებები და რბოლები

დაწერე BeamMP-ის სერვერის plugin-ები Lua-ზე: ივენთების სასიცოცხლო ციკლი, ჩატის ბრძანებები, ტაიმერები, მანქანების წესები, მონაცემების შენახვა და მომუშავე საკონტროლო წერტილების რბოლა.

0 მკითხველი

BeamMP-ს თამაშის რეჟიმები არ აქვს. BeamMP-ის სერვერი მანქანების მდგომარეობას გადასცემს და Lua-ს უშვებს, და ყველაფერი, რაც სერვერს ავტოსადგომზე მეტად აქცევს - რბოლები, whitelist-ები, მანქანების წესები, ჩატის ბრძანებები, Discord-ის ხიდი - არის Lua plugin Resources/Server-ში. სერვერის მხარის API იმდენად პატარაა, რომ ერთ საღამოში ისწავლება: არეგისტრირებ დამმუშავებლებს ათიოდე ივენთისთვის, იძახებ ორ ათეულამდე MP.* ფუნქციას და იყენებ ტაიმერებს ყველაფრისთვის, რაც დროში ხდება. ეს ნამდვილი რბოლების ჩასატარებლადაც საკმარისია, რადგან სერვერს შეუძლია MP.GetPositionRaw-ით წაიკითხოს ყოველი მანქანის ბოლოს გაგზავნილი პოზიცია, და საკონტროლო წერტილების სისტემას მეტი არაფერი სჭირდება. ეს გზამკვლევი მოიცავს plugin-ების მოდელს, ივენთებსა და ფუნქციებს, რომლებსაც რეალურად გამოიყენებ, და სრულ სარბოლო plugin-ს უკუთვლით, კარიბჭეებით, წრეებით და ფინიშის დროებით.

BeamMP-ის სერვერის გზამკვლევი მოიცავს ინსტალაციას, ServerConfig.toml-ს, რუკებსა და კლიენტის მოდებს; თუ შენი სერვერი ჯერ არ მუშაობს, ჯერ ის წაიკითხე. აქ ყველაფერი 3.x სერვერისთვისაა, რომელიც Lua 5.3-ს იყენებს. 2.x API-სთვის ან დიდი ხნის წინ მიტოვებული საზოგადოების ფრეიმვორკებისთვის დაწერილი ძველი plugin-ები ხშირად არ იტვირთება, და ეს პირველია, რაც უნდა შეამოწმო, როცა ჩამოტვირთული სკრიპტი არაფერს აკეთებს.

როგორ იტვირთება სერვერის plugin-ები#

Plugin არის საქაღალდე Resources/Server-ის ქვეშ. სერვერი ტვირთავს ამ საქაღალდის ძირში არსებულ ყველა .lua ფაილს ანბანური რიგით და ზედა დონის კოდს ერთხელ უშვებს. ქვესაქაღალდეებში არსებული ფაილები იგნორირდება, თუ მათ require-ით არ ჩატვირთავ, და სწორედ იქ არის ადგილი დამხმარე ფუნქციებისა და მონაცემებისთვის.

code
Resources/  Client/                 mod zips sent to players  Server/    RaceControl/      main.lua            loaded at start      lib/gates.lua       only if main.lua requires it    Whitelist/      main.lua

ყოველი plugin საკუთარ Lua state-ში მუშაობს. ორ plugin-ს ერთმანეთის ცვლადების წაკითხვა არ შეუძლია; ისინი ივენთებით ურთიერთობენ, რაც ქვემოთაა განხილული. plugin-ის შიგნით სქემა ყოველთვის ერთია: განსაზღვრე გლობალური ფუნქციები, შემდეგ კი დაარეგისტრირე ისინი ივენთების სახელებზე MP.RegisterEvent(eventName, functionName)-ით. მეორე არგუმენტი გლობალური ფუნქციის სახელია სტრიქონის სახით და არა თავად ფუნქცია, ამიტომაც local-ად გამოცხადებული დამმუშავებელი ჩუმად არასდროს ეშვება.

სერვერი plugin-ის საქაღალდესაც უთვალთვალებს. plugin-ის ძირში ფაილის შენახვა onFileChanged-ს იწვევს და ამ plugin-ის state-ს ხელახლა ტვირთავს, ასე რომ, სერვერის გადატვირთვის გარეშე შეგიძლია იმუშაო. ხელახალი ჩატვირთვა plugin-ის ცვლადებს შლის, ამიტომ მიმდინარე რბოლა ქრება, როცა main.lua-ს შეინახავ; არედაქტირე რბოლებს შორის და არა მათ დროს.

გამონატანი სერვერის კონსოლში მიდის. print წერს [LUA] პრეფიქსით, ხოლო Util.LogInfo, Util.LogWarn, Util.LogError და Util.LogDebug შესაბამის დონეზე წერენ. სინტაქსური შეცდომა ჩატვირთვისას ფაილითა და ხაზით ცხადდება, და plugin გამოტოვებულია. თუ plugin „არაფერს აკეთებს", პასუხი კონსოლშია, ჩატვირთვის მომენტიდან - კონსოლის კითხვა აღწერს, როგორ გააკეთო ეს ეფექტურად.

ივენთები, რომლებსაც გამოიყენებ#

onPlayerAuthდაშვება ან უარიonPlayerConnectingonPlayerJoinსრულად შემოსულიonVehicleSpawnდაშვება ან გაუქმებაonChatMessageბრძანებებიonPlayerDisconnect
მოთამაშის გზა BeamMP-ის plugin ივენთებში
ივენთიარგუმენტებიგაუქმებადი
onInitარცერთიარა
onPlayerAuthname, role, is_guest, identifiersკი - დააბრუნე 1 ან მიზეზის სტრიქონი
onPlayerConnectingplayer_idარა
onPlayerJoiningplayer_idარა
onPlayerJoinplayer_idარა
onPlayerDisconnectplayer_idარა
onChatMessageplayer_id, player_name, messageკი - დააბრუნე 1 შეტყობინების დასამალად
onVehicleSpawnplayer_id, vehicle_id, dataკი - დააბრუნე 1 spawn-ზე უარის სათქმელად
onVehicleEditedplayer_id, vehicle_id, dataკი - დააბრუნე 1 ცვლილებაზე უარის სათქმელად
onVehicleResetplayer_id, vehicle_id, dataარა
onVehicleDeletedplayer_id, vehicle_idარა
onConsoleInputinputარა
onShutdownარცერთიარა

ამათგან სამი აკეთებს სამუშაოს უმეტესობას. onPlayerAuth ეშვება მანამ, სანამ მოთამაშეს ID ექნება, ამიტომ whitelist-ები და ბანები აქ ცხოვრობს; ის იღებს identifiers ცხრილს მოთამაშის beammp ID-ით, IP-ით და, თუ მიბმულია, Discord ID-ით. onChatMessage-ით ხორციელდება ჩატის ყოველი ბრძანება - ბრძანებების რეესტრი არ არსებობს, ტექსტს თავად არჩევ. onVehicleSpawn და onVehicleEdited მანქანების წესების ადგილია: data არგუმენტი მანქანის კონფიგურაციას JSON-ად შეიცავს, ასე რომ, შეგიძლია უარი თქვა მოდელზე, ნაწილზე ან პიტების გარეთ spawn-ზე.

გაუქმება დაბრუნებული მნიშვნელობით ხდება და მხოლოდ მისით. onChatMessage-იდან 1-ის დაბრუნება შეტყობინებას ყველასგან მალავს, რაც ბრძანებებისთვის გინდა - /start-ის ნახვა ჩატში არავის სჭირდება. თუ არაფერს დააბრუნებ, შეტყობინება გაივლის.

ფუნქციები, რომელთა ცოდნაც ღირს#

ფუნქციარას აკეთებს
MP.SendChatMessage(id, text)ჩატი ერთ მოთამაშეს, ან ყველას -1-ით
MP.GetPlayers()ცხრილი: მოთამაშის ID - სახელი
MP.GetPlayerName(id)სახელი ID-ის მიხედვით
MP.GetPlayerIdentifiers(id)beammp, ip და discord, თუ ცნობილია
MP.IsPlayerGuest(id)აქვს თუ არა მოთამაშეს BeamMP ანგარიში
MP.GetPlayerVehicles(id)ცხრილი: მანქანის ID - მანქანის მონაცემები
MP.GetPositionRaw(id, vid)ბოლოს გაგზავნილი pos, rot, vel, rvel, ping და tim
MP.RemoveVehicle(id, vid)შლის მანქანას
MP.DropPlayer(id, reason)აგდებს შეტყობინებით
MP.CreateEventTimer(name, ms)იწვევს ივენთს name ყოველ ms მილიწამში
MP.CancelEventTimer(name)აჩერებს მას
MP.TriggerLocalEvent(name, ...)იძახებს დამმუშავებლებს ამ plugin-ში, სინქრონულად
MP.TriggerGlobalEvent(name, ...)იძახებს დამმუშავებლებს ყველა plugin-ში
MP.TriggerClientEvent(id, name, data)უგზავნის სტრიქონს კლიენტის მხარის მოდს
MP.Set(MP.Settings.MaxCars, n)ცვლის კონფიგურაციის მნიშვნელობას გადატვირთვამდე
MP.CreateTimer()წამზომი :Start()-ით და :GetCurrent()-ით წამებში

პოზიციები ვექტორებია Lua-ს მასივებში, ასე რომ, raw.pos[1], raw.pos[2] და raw.pos[3] არის x, y და z. BeamNG z-ს ზემოთ მიმართულებად იყენებს, ამიტომ მიწის სიბრტყე x და y-ია. Util.JsonEncode და Util.JsonDecode ცხრილებს სტრიქონებად აქცევს და პირიქით, რაც გჭირდება როგორც მონაცემების შესანახად, ისე კლიენტის მოდებთან სალაპარაკოდ. FS.Exists, FS.CreateDirectory და FS.ConcatPaths ფაილებსა და გზებს პლატფორმისგან დამოუკიდებლად ამუშავებს.

სიგნატურები სერვერის ვერსიებს შორის იცვლება, და შენი ვერსიის ოფიციალური სკრიპტინგის ცნობარი ჯობია ბლოგის ნებისმიერ ცხრილს, ამის ჩათვლით.

ჩატის ბრძანებები და ადმინები#

ჩაშენებული ადმინების სია არ არსებობს, ამიტომ plugin წყვეტს, ვინ არის ადმინი. დააფუძნე ეს beammp იდენტიფიკატორზე და არასდროს ნაჩვენებ სახელზე, რომლის შეცვლაც მოთამაშეებს შეუძლიათ:

Resources/Server/Admin/main.lua
local ADMINS = { ["1234567"] = true }local function isAdmin(pid)    local ids = MP.GetPlayerIdentifiers(pid)    return ids ~= nil and ADMINS[tostring(ids.beammp)] == trueendfunction AdminChat(pid, name, msg)    local cmd, arg = msg:match("^/(%S+)%s*(.*)$")    if not cmd then return end    if not isAdmin(pid) then return end    if cmd == "kick" then        for id, pname in pairs(MP.GetPlayers()) do            if pname == arg then MP.DropPlayer(id, "Kicked by " .. name) end        end    elseif cmd == "cars" then        MP.Set(MP.Settings.MaxCars, tonumber(arg) or 1)        MP.SendChatMessage(-1, "Car limit is now " .. arg)    else        return    end    return 1endMP.RegisterEvent("onChatMessage", "AdminChat")

შაბლონი ^/(%S+)%s*(.*)$ ყოფს /kick SomeName-ს ბრძანებად და ხაზის დანარჩენ ნაწილად. უცნობი ბრძანებები გაივლის და ჩატში ჩნდება, რაც უფრო მეგობრული ჩავარდნაა. MP.Set პარამეტრს მხოლოდ გაშვებული სერვერისთვის ცვლის; ServerConfig.toml-ის მნიშვნელობა შემდეგი გადატვირთვისას ბრუნდება, რაც მას ივენთების საღამოებისთვის იდეალურს ხდის.

მანქანების წესები spawn-ისას#

onVehicleSpawn-ის data არგუმენტი სტრიქონია, რომელსაც JSON-ის წინ პრეფიქსი აქვს, ამიტომ დეკოდირებამდე პირველი ფიგურული ფრჩხილიდან მოჭერი. მის მიმართ წესების დაწერამდე ერთი spawn ჩაწერე ლოგში - ველების სახელები თამაშიდან მოდის, და მათი გაგების ყველაზე უსაფრთხო გზა ნახვაა:

Resources/Server/CarRules/main.lua
local ALLOWED = { covet = true, sunburst = true }function CarSpawn(pid, vid, data)    local start = data:find("{")    if not start then return end    local cfg = Util.JsonDecode(data:sub(start))    print("spawn by " .. MP.GetPlayerName(pid) .. ": " .. tostring(cfg.jbm))    if cfg.jbm and not ALLOWED[cfg.jbm] then        MP.SendChatMessage(pid, "Only the race cars are allowed tonight.")        return 1    endendMP.RegisterEvent("onVehicleSpawn", "CarSpawn")MP.RegisterEvent("onVehicleEdited", "CarSpawn")

იგივე დამმუშავებელი onVehicleEdited-ზეც დაარეგისტრირე, თორემ მოთამაშეები დაშვებულ მანქანას გამოიძახებენ და შემდეგ მანქანის კონფიგურატორში გაცვლიან. უარყოფილი spawn სხვა მოთამაშეების ეკრანებზე არაფერს ტოვებს; შემდეგ MP.RemoveVehicle-ით წაშლილი მანქანა კი ჯერ წამიერად ჩნდება, ამიტომ, სადაც შეგიძლია, უარი spawn-ისას თქვი.

საკონტროლო წერტილების სარბოლო plugin#

რბოლებს ოთხი რამ სჭირდება: მონაწილეების სია, უკუთვლა, გზა იმის გასაგებად, როდის გაივლის მანქანა წერტილს, და საათი. სერვერს ოთხივე აქვს. კარიბჭეები რუკაზე წერტილებია რადიუსით; ყოველმა მრბოლელმა ისინი რიგრიგობით უნდა გაიაროს, და ბოლო კარიბჭის გავლა წრეს ასრულებს. პოზიციები ტაიმერით მოწმდება.

Resources/Server/RaceControl/main.lua
local ADMINS = { ["1234567"] = true }local GATES = {                -- fill in with /pos, finish line last    { x = 0.0, y = 0.0, r = 15 },    { x = 0.0, y = 0.0, r = 15 },}local LAPS = 3local race = { state = "idle", count = 0, racers = {}, finished = 0 }local clock = MP.CreateTimer()local function say(pid, text) MP.SendChatMessage(pid, "[Race] " .. text) endlocal function isAdmin(pid)    local ids = MP.GetPlayerIdentifiers(pid)    return ids ~= nil and ADMINS[tostring(ids.beammp)] == trueendlocal function firstVehicle(pid)    local vehicles = MP.GetPlayerVehicles(pid)    if vehicles == nil then return nil end    for vid, _ in pairs(vehicles) do return vid endendlocal function reset()    MP.CancelEventTimer("RaceCountdown")    MP.CancelEventTimer("RaceTick")    race = { state = "idle", count = 0, racers = {}, finished = 0 }endfunction RaceChat(pid, name, msg)    local cmd = msg:match("^/(%S+)")    if cmd == "join" and race.state == "idle" then        race.racers[pid] = { gate = 1, lap = 1, done = false }        say(-1, name .. " is on the grid")    elseif cmd == "pos" then        local vid = firstVehicle(pid)        local raw = vid and MP.GetPositionRaw(pid, vid)        if raw and raw.pos then            say(pid, string.format("x=%.1f y=%.1f", raw.pos[1], raw.pos[2]))        end    elseif cmd == "start" and isAdmin(pid) and race.state == "idle" then        race.state = "countdown"        race.count = 5        MP.CreateEventTimer("RaceCountdown", 1000)    elseif cmd == "stop" and isAdmin(pid) then        reset()        say(-1, "Race cancelled")    else        return    end    return 1endfunction RaceCountdown()    if race.count > 0 then        say(-1, tostring(race.count))        race.count = race.count - 1        return    end    MP.CancelEventTimer("RaceCountdown")    race.state = "running"    clock:Start()    say(-1, "GO")    MP.CreateEventTimer("RaceTick", 200)endfunction RaceTick()    for pid, r in pairs(race.racers) do        local vid = firstVehicle(pid)        local raw = vid and MP.GetPositionRaw(pid, vid)        if raw and raw.pos and not r.done then            local g = GATES[r.gate]            local dx, dy = raw.pos[1] - g.x, raw.pos[2] - g.y            if dx * dx + dy * dy <= g.r * g.r then                r.gate = r.gate + 1                if r.gate > #GATES then                    r.gate = 1                    if r.lap >= LAPS then                        r.done = true                        race.finished = race.finished + 1                        say(-1, string.format("P%d %s %.2fs", race.finished,                            MP.GetPlayerName(pid), clock:GetCurrent()))                    else                        r.lap = r.lap + 1                    end                end            end        end    endendfunction RaceLeave(pid) race.racers[pid] = nil endMP.RegisterEvent("onChatMessage", "RaceChat")MP.RegisterEvent("RaceCountdown", "RaceCountdown")MP.RegisterEvent("RaceTick", "RaceTick")MP.RegisterEvent("onPlayerDisconnect", "RaceLeave")

დასაყენებლად ტრასის თითოეულ წერტილამდე მიდი - რამდენიმე ასეული მეტრის დაშორებით, ისეთ მოსახვევებში, რომლებსაც ვერავინ მოჭრის - და აკრიფე /pos. დააკოპირე x და y GATES-ში სვლის თანმიმდევრობით, სტარტ-ფინიშის ხაზი კი ბოლო ჩანაწერად ჩასვი, რომ გრიდიდან დაძრული მანქანები ჯერ პირველი კარიბჭისკენ წავიდნენ. შეინახე ფაილი, plugin ხელახლა ჩაიტვირთება, და მრბოლელები /join-ს კრეფენ, სანამ ადმინი /start-ს აკრეფს.

რამდენად ზუსტია ეს, გულწრფელად

სერვერმა მხოლოდ ის იცის, სად იყო მანქანა ბოლო განახლებისას, ამიტომ სიზუსტე ორ რამეზეა დამოკიდებული. ყოველ 200 ms-ში შემოწმება ნიშნავს, რომ 200 კმ/სთ-ზე მიმავალი მანქანა შემოწმებებს შორის დაახლოებით 11 მეტრს გადის, ამიტომაც არის კარიბჭის რადიუსი 15 მეტრი და არა სამი. და ყოველი პოზიცია ისეთივე ძველია, როგორც ამ მოთამაშის ქსელური დაყოვნება, ასე რომ, 150 ms ping-ის მქონე მძღოლის დრო ოდნავ გვიან ითვლება. პრაქტიკაში ეს წამის რამდენიმე მეათედია, რაც მხიარული კვირის რბოლისთვის ნორმალურია და არა ჩემპიონატისთვის, რომელიც მეასედებით წყდება.

მისი გამკაცრების ორი გზა არსებობს. tick-ის 100 ms-მდე შემცირება მანძილის პრობლემას ორჯერ ამცირებს და სამუშაოს ორჯერ ზრდის; რამდენიმე მრბოლელით ეს ფასი უმნიშვნელოა, ორმოცით - არა. ნამდვილი სიზუსტისთვის ამოცნობა ყოველი მოთამაშის საკუთარ მანქანაზე უნდა მოხდეს: კლიენტის მხარის მოდი Resources/Client-ში ლოკალურად ადევნებს თვალს კარიბჭის გადაკვეთას და TriggerServerEvent-ით აცნობებს, სერვერის plugin კი მათ MP.RegisterEvent-ით იღებს დამმუშავებლით, რომელიც მოთამაშის ID-სა და მონაცემების სტრიქონს იღებს. ეს მეტი სამუშაოა და კლიენტის მოდს მოითხოვს, რომელსაც შენ შეინახავ, ამიტომაც საზოგადოების სარბოლო სერვერების უმეტესობა უბრალო ივენთებისთვის სერვერის მხარის მარტივ მიდგომას იყენებს, ლიგებისთვის კი სპეციალურ timing მოდს.

როგორ აქციო რბოლა ნამდვილ რბოლად

რამდენიმე დამატება, რომელიც ცოტა ჯდება:

  • რბოლამდე გაყინე შემადგენლობა: MP.Set-ით MaxCars დააყენე 1-ზე, რომ რბოლის შუაში არავინ გამოიძახოს მეორე მანქანა, და შემდეგ აღადგინე.
  • აკრძალე reset-ები რბოლის დროს. onVehicleReset გაუქმებადი არ არის, მაგრამ დისკვალიფიკაცია შეგიძლია: თუ მრბოლელი reset-ს აკეთებს, როცა race.state არის running, დააყენე done და გამოაცხადე.
  • შედეგები Discord-ში გამოაცხადე: ჩაწერე ისინი ფაილში, რომელსაც ცალკე ბოტი კითხულობს, ან გამოიყენე webhook-ებისთვის შექმნილი plugin - Discord webhook-ები სერვერის სტატუსისთვის მიმღებ მხარეს აღწერს.
  • ტრასის კონფიგურაციები მონაცემებად შეინახე. ყოველი ტრასის კარიბჭეები lib/-ში ცალკე ფაილში ჩადე და სწორი require-ით ჩატვირთე, რომ ადგილის შეცვლა plugin-ის რედაქტირებას არ ნიშნავდეს.

მონაცემების შენახვა გადატვირთვებს შორის#

Plugin-ის ცვლადები სერვერთან ერთად კვდება. ყველაფერი, რაც უნდა გადარჩეს - წრის რეკორდები, ბანების სია, რბოლების ისტორია - დისკზე უნდა ჩაიწეროს. Lua-ს io ბიბლიოთეკა და Util.JsonEncode საკმარისია:

lua
local DIR = "Resources/Server/RaceControl/data"local FILE = FS.ConcatPaths(DIR, "records.json")local function loadRecords()    if not FS.Exists(FILE) then return {} end    local f = io.open(FILE, "r")    local text = f:read("*a")    f:close()    return Util.JsonDecode(text) or {}endlocal function saveRecords(records)    if not FS.Exists(DIR) then FS.CreateDirectory(DIR) end    local f = io.open(FILE, "w")    f:write(Util.JsonEncode(records))    f:close()end

გზები სერვერის სამუშაო დირექტორიის მიმართაა. plugin-ის საკუთარ ძირეულ საქაღალდეში ფაილის ჩაწერამ შეიძლება ამ plugin-ის hot reload გამოიწვიოს, რაც წაშლის რბოლას, რომელმაც ახლახან რეკორდი ჩაწერა - ამიტომ მონაცემების ფაილები ჩადე ქვესაქაღალდეში, მაგალითად data/-ში, როგორც ზემოთაა, რომელსაც სერვერი plugin-ის კოდად არ ტვირთავს. ეს BeamMP-ის plugin-ების შემუშავებაში ყველაზე დამაბნეველი ხარვეზია და ისე გამოიყურება, თითქოს სერვერი plugin-ს შემთხვევით გადატვირთავს.

ეს მონაცემების ფაილები შენი სერვერის ის ნაწილია, რომელსაც ხელახლა ვერ ჩამოტვირთავ. Resources/Server-ის backup გააკეთე რედაქტირების ყოველი სესიის წინ და სერვერის ყოველი განახლების წინ; backup-ები, რომლებიც მართლა აღდგება არის არგუმენტი იმისთვის, რომ ერთი მართლა გამოცადო.

კომუნიკაცია plugin-ებს შორის და კლიენტის მოდებთან#

Plugin-ები იზოლირებულია, ამიტომ whitelist plugin-ს სარბოლო plugin-ის ფუნქციის გამოძახება არ შეუძლია. მათ ივენთები აკავშირებს: MP.TriggerGlobalEvent("RaceFinished", pid, time) ერთ plugin-ში იძახებს RaceFinished-ზე დარეგისტრირებულ ყველა დამმუშავებელს ყველა plugin-ში. MP.TriggerLocalEvent იგივეს აკეთებს ერთი plugin-ის შიგნით და სინქრონულად ბრუნდება, რაც დიდი plugin-ის ფაილებად დასაყოფად მოსახერხებელია.

კლიენტის მოდები საპირისპირო მიმართულებაა. Resources/Client-ში არსებული zip შეიძლება შეიცავდეს Lua-ს, რომელიც ყოველი მოთამაშის თამაშში ეშვება, AddEventHandler-ით არეგისტრირებს დამმუშავებლებს და TriggerServerEvent-ით აგზავნის მონაცემებს. სერვერი აგზავნის MP.TriggerClientEvent(pid, name, data)-ით, ან ცხრილისთვის MP.TriggerClientEventJson-ით. ყველაფერი სტრიქონებად მოგზაურობს, ამიტომ ცხრილები ორივე მხარეს JSON-ად დაშიფრე. გამოიყენე ეს ინტერფეისისთვის - უკუთვლა ეკრანზე ჩატის ნაცვლად, წრის ტაიმერი, ლიდერბორდი - ხოლო ავტორიტეტი სერვერზე დატოვე: კლიენტის მოდი, რომელიც საკუთარ წრის დროებს აცხადებს, შეიძლება გაასწოროს მოთამაშემ, რომელიც მას უშვებს.

უსაფრთხოება, წარმადობა და plugin-ების მუშა მდგომარეობაში შენარჩუნება#

Plugin-ები სერვერის სრული უფლებებით მუშაობს და ხედავს ჩატის ყოველ ხაზსა და ყოველ შეერთებას. რასაც ჩამოტვირთავ, წაიკითხე, სანამ ავტომატურად ჩატვირთვად საქაღალდეში ჩააგდებ, და უპირატესობა მიანიჭე plugin-ებს საჯარო კოდის რეპოზიტორიით. მოდიფიცირებული სერვერის სისუფთავე ზოგადი ჰიგიენაა, ხოლო მავნე plugin-ები და მოდები აღწერს, რისი გაკეთება შეუძლია მტრულ plugin-ს.

BeamMP-ში წარმადობის პრობლემები თითქმის ყოველთვის არის დამმუშავებელი, რომელიც ზედმეტად ბევრს და ზედმეტად ხშირად აკეთებს. 50 ms-იანი ტაიმერი, რომელიც ყველა მოთამაშეზე გადის და JSON-ს დეკოდირებს, მოულოდნელ CPU-დ გამოჩნდება. Util.DebugExecutionTime() თითოეული დამმუშავებლის დროის სტატისტიკას აბრუნებს, რაც ვარაუდს რიცხვად აქცევს. დამმუშავებლის შიგნით MP.Sleep დიდი ხნით არასდროს გამოიძახო; ის ამ plugin-ს ბლოკავს.

RE:NODE-ზე BeamMP-ის გეგმა გაძლევს კონსოლს ისტორიით plugin-ის გამონატანის წასაკითხად, ფაილ-მენეჯერის რედაქტორს სინტაქსის მონიშვნით main.lua-სთვის, და SFTP-ს, თუ ლოკალურად მუშაობა გირჩევნია. plugin-ის ფაილის შენახვა მას hot-reload-ით ტვირთავს, ასე რომ, რედაქტირება-შემოწმების ციკლს გადატვირთვა არ სჭირდება, ხოლო რედაქტირების სესიამდე აღებული backup-ის სლოტი მთელი Resources საქაღალდის „გაუქმების" ღილაკია.

BeamMP-ის სერვერის განახლების შემდეგ, plugin-ებით სავსე სერვერის გადატვირთვამდე, changelog-ში სკრიპტინგის ცვლილებები წაიკითხე. რა ვქნა, როცა მოდის განახლება რამეს ამტვრევს აღდგენის თანმიმდევრობაა, თუ ამას მწარე გამოცდილებით გაიგებ.

პრობლემების მოგვარება#

დამმუშავებელი არასდროს ეშვება. ფუნქცია local-ია, MP.RegisterEvent-ში არასწორად წერია, ან ივენთის სახელს არასწორი რეგისტრი აქვს. ივენთების სახელები რეგისტრისადმი მგრძნობიარეა.

Plugin შემთხვევით თავისით ირესეტება. ის ფაილს საკუთარ ძირეულ საქაღალდეში წერს და hot reload-ს იწვევს. გადაიტანე მონაცემები ქვესაქაღალდეში.

`attempt to index a nil value` პოზიციებზე. მოთამაშეს მანქანა არ აქვს, ან MP.GetPositionRaw-მა ცარიელი შედეგი დააბრუნა. გამოყენებამდე შეამოწმე raw და raw.pos.

ბრძანებები ჩატში ჩნდება. დამმუშავებელი დამუშავებული ბრძანებებისთვის 1-ს არ აბრუნებს.

კარიბჭეები არასდროს ირთვება. კოორდინატები x და z-ად დაკოპირდა x და y-ის ნაცვლად, ან რადიუსი იმ მანძილზე ნაკლებია, რომელსაც მანქანა შემოწმებებს შორის გადის.

ძველი plugin არაფერს აკეთებს. ის 2.x API-ს ეყრდნობა. მოძებნე MP.RegisterEvent დამმუშავებლების სტრიქონული სახელებით; ყველაფერს სხვას გადაწერა სჭირდება.

FAQ#

აქვს BeamMP-ს ჩაშენებული სარბოლო რეჟიმები?

არა. სერვერს თამაშის რეჟიმები საერთოდ არ აქვს. რბოლები, დროის აღრიცხვა და ლიდერბორდები Lua plugin-ებია, თავად დაწერილი ან ჩამოტვირთული, ზოგჯერ კლიენტის მხარის მოდით ეკრანზე დროის საჩვენებლად.

Lua-ს რომელ ვერსიას იყენებს BeamMP-ის სერვერი?

Lua 5.3-ს 3.x სერვერზე. ძველი 2.x API-სთვის დაწერილ plugin-ებს გადაწერა სჭირდება, ძირითადად სტრიქონზე დაფუძნებულ MP.RegisterEvent დამმუშავებლებზე და ივენთების მიმდინარე სახელებზე.

მოთამაშეებს სერვერის plugin-ებისთვის რამის დაყენება სჭირდებათ?

Resources/Server-ში არსებული სერვერის მხარის plugin-ებისთვის - არა. მოთამაშეებამდე მხოლოდ Resources/Client-ში არსებული კლიენტის მოდები აღწევს, და ისინი შემოსვლისას ავტომატურად ჩამოიტვირთება.

რამდენად ზუსტია სერვერის მხარის წრის დროის აღრიცხვა?

წამის რამდენიმე მეათედის ფარგლებში, რასაც შემოწმების ინტერვალი და ყოველი მოთამაშის ping ზღუდავს. უბრალო რბოლებისთვის ეს ნორმალურია. შეჯიბრებით დროის აღრიცხვას კლიენტზე ამოცნობა სჭირდება, რომელიც სერვერს ეცნობება.

შემიძლია plugin-ით პაროლის დამატება?

პაროლის ველი არ არსებობს, მაგრამ onPlayerAuth-ზე შეგიძლია უარი უთხრა ყველას, ვისი beammp იდენტიფიკატორიც შენს სიაში არ არის, რაც პაროლზე მკაცრია, რადგან მისი გაზიარება შეუძლებელია.

plugin-ის რედაქტირების შემდეგ სერვერის გადატვირთვა მჭირდება?

არა. plugin-ის ძირეულ საქაღალდეში ფაილის შენახვა ამ plugin-ს ხელახლა ტვირთავს. მისი ცვლადები ნულდება, ამიტომ ეს ივენთებს შორის გააკეთე და არა მათ დროს.


კომენტარები

სრულიად ანონიმურად: ანგარიშის, ელფოსტის და cookie-ის გარეშე. ინახება მხოლოდ სახელი, ტექსტი და დრო - სხვა არაფერი. ბმულების რაოდენობა ლიმიტირებულია.

0/2000