Server và client
Mỗi game KHIM chạy ở hai nơi cùng lúc:
- Server - mỗi phiên game một server, do KHIM chạy. Các Script của nó nắm game: world, máu, xu, điểm.
- Thiết bị của từng người chơi - app KHIM hoặc trình duyệt. Các ClientScript lo phím bấm, UI, camera và hiệu ứng của người chơi đó.
Ai thấy gì
| Script trên server | ClientScript | |
|---|---|---|
| Thay đổi một part | Tới mọi người chơi | Chỉ trên màn hình người chơi đó |
players.me, input, camera, gui của người chơi | - | Có |
data (lưu), chat.send, achievements, destruction | Có | - |
| ServerService và StorageService | Có | Không bao giờ: chúng không được gửi đi |
Vì vậy một cánh cửa do ClientScript mở chỉ mở với một người - và vì server nắm va chạm, người đó vẫn đâm vào nó. Những gì quan trọng phải xảy ra trong Script.
Quy tắc duy nhất
Server quyết định. ClientScript có thể xin, nhưng người chơi có thể chạy app đã bị sửa và gửi bất cứ thứ gì. Script không bao giờ được lấy số lượng, giá tiền, mục tiêu hay kết quả từ người chơi. Nó kiểm tra người chơi có được làm việc đó bây giờ không - đủ gần, còn sống, hết thời gian hồi, đủ tiền - và tự tính con số.
Tin nhắn: net
net gửi tin nhắn theo tên giữa server và người chơi. Giá trị có thể là nil, boolean, số, chuỗi, kiểu giá trị (vec3, màu...), part, người chơi và bảng chứa những thứ đó - tối đa 16 KB.
Người chơi gửi server
-- ClientScript: xin mua một thanh kiếm
net.send("buy", "Sword")-- Script: quyết định
local PRICES = {Sword = 50, Shield = 30}
net.on("buy", function(player, item) -- người gửi đứng đầu
local price = PRICES[item]
if not price then return end -- không phải món ta bán
if player.stats.Coins < price then return end
player.stats.Coins -= price
shared.Items[item]:clone().parent = player.backpack
end)Server gửi người chơi
-- Script
net.send(player, "coinCollected", player.stats.Coins) -- một người
net.sendAll("roundStarted", 2) -- mọi người-- ClientScript
net.on("coinCollected", function(total)
label.text = "Xu: " .. total
end)Hỏi và chờ câu trả lời
net.call gửi câu hỏi từ ClientScript và chờ server trả lời; net.handle trả lời nó:
-- Script
net.handle("redeem", function(player, code)
if code == "WELCOME" and not player.stats.Redeemed then
player.stats.Coins += 100
player.stats.Redeemed = true
return true, "Đã cộng 100 xu!"
end
return false, "Mã này không dùng được."
end)-- ClientScript
local ok, message = net.call("redeem", box.text)
resultLabel.text = messageLỗi trong hàm xử lý trở thành lỗi ở phía gọi.
Giới hạn
Một người chơi gửi tối đa 120 tin nhắn mỗi giây; server bỏ phần còn lại. Một số là NaN hoặc vô cực sẽ khiến cả tin nhắn bị từ chối, và part từ ServerService hay StorageService tới nơi thành nil. Dù vậy - hãy kiểm tra mọi thứ một tin nhắn nói.
Ví dụ: một thanh kiếm
-- Script (ServerService): client xin, server quyết
local lastSwing = {}
net.on("swing", function(player)
local now = time()
if (lastSwing[player.name] or 0) + 0.5 > now or player.health <= 0 then return end
lastSwing[player.name] = now
for _, other in players.all() do
if other.name ~= player.name and (other.position - player.position).magnitude < 6 then
other:damage(20) -- con số của server, không phải của client
end
end
end)-- ClientScript: chỉ gửi cú bấm, không gì khác
input.onKey("F", function()
net.send("swing")
end)Client không gửi sát thương, không gửi mục tiêu: server biết mọi người đang ở đâu.
Đặt script ở đâu
| Mục chứa | Ai có | Đặt vào đây |
|---|---|---|
| World | Server và mọi người chơi | Part, và Script thuộc về một part |
ReplicationService (shared) | Server và mọi người chơi | Model để clone, Module cả hai phía dùng |
| ServerService | Chỉ server | Script logic game, Module bí mật |
| ClientService | Mọi người chơi | ClientScript không gắn với UI |
| StorageService | Chỉ server, không gì chạy | Model và Module cho Script của server |
| UI | Mọi người chơi | Canvas và ClientScript điều khiển chúng |
ClientScript không bí mật
Người chơi nhận mọi ClientScript và mọi Module mà chúng require được (đã bỏ chú thích). Giữ bí mật - mã code, danh sách admin, giá bạn kiểm tra - trong ServerService hoặc StorageService.
Đang ở phía nào
if game.isServer then print("server") end
if game.isClient then print("thiết bị người chơi") endMỗi khung hình
game.frame:connect(function(dt) -- dt: số giây từ khung hình trước
spinner.rotation += vec3(0, 90 * dt, 0)
end)Trên server nó chạy mỗi bước (60 lần một giây); trên thiết bị người chơi, mỗi khung hình được vẽ.
Khi server tắt
game.closing:connect(function()
-- mọi người chơi vẫn còn đây: lưu những gì game giữ chung cho mọi người
end)Server chờ các hàm xử lý closing (và các hàm players.left chạy sau đó) tối đa 20 giây. Xem Lưu dữ liệu.