ไคลเอ็นต์ Godot สำหรับเซิร์ฟเวอร์ Nakama ที่เขียนด้วย GDScript
Nakama เป็นเซิร์ฟเวอร์โอเพ่นซอร์สที่ออกแบบมาเพื่อขับเคลื่อนเกมและแอปสมัยใหม่ คุณสมบัติต่างๆ ได้แก่ บัญชีผู้ใช้ แชท โซเชียล ระบบจับคู่ ผู้เล่นหลายคนแบบเรียลไทม์ และอื่นๆ อีกมากมาย
ไคลเอ็นต์นี้ใช้ตัวเลือก API และซ็อกเก็ตแบบเต็มกับเซิร์ฟเวอร์ เขียนด้วย GDScript เพื่อรองรับ Godot Engine 4.0+
เอกสารฉบับเต็มออนไลน์อยู่ - https://heroiclabs.com/docs
ขณะนี้คุณกำลังดูไคลเอนต์ Nakama เวอร์ชัน Godot 4 สำหรับ Godot
หากคุณใช้ Godot 3 คุณจะต้องใช้สาขา 'godot-3' บน GitHub
คุณจะต้องตั้งค่าเซิร์ฟเวอร์และฐานข้อมูลก่อนจึงจะสามารถเชื่อมต่อกับไคลเอนต์ได้ วิธีที่ง่ายที่สุดคือใช้ Docker แต่ดูเอกสารประกอบของเซิร์ฟเวอร์เพื่อดูตัวเลือกอื่นๆ
ติดตั้งและรันเซิร์ฟเวอร์ ทำตามคำแนะนำเหล่านี้
ดาวน์โหลดไคลเอนต์จากหน้าเผยแพร่และนำเข้าไปยังโปรเจ็กต์ของคุณ คุณยังสามารถดาวน์โหลดได้จากที่เก็บสินทรัพย์
เพิ่ม Nakama.gd
singleton (ใน addons/com.heroiclabs.nakama/
) เป็นการโหลดอัตโนมัติใน Godot
ใช้ข้อมูลรับรองการเชื่อมต่อเพื่อสร้างวัตถุไคลเอนต์โดยใช้ซิงเกิลตัน
extends Node
func _ready ():
var scheme = "http"
var host = "127.0.0.1"
var port = 7350
var server_key = "defaultkey"
var client := Nakama . create_client ( server_key , host , port , scheme )
วัตถุไคลเอนต์มีวิธีการมากมายในการรันคุณสมบัติต่าง ๆ ในเซิร์ฟเวอร์หรือเปิดการเชื่อมต่อซ็อกเก็ตเรียลไทม์กับเซิร์ฟเวอร์
มีหลายวิธีในการตรวจสอบสิทธิ์กับเซิร์ฟเวอร์ การรับรองความถูกต้องสามารถสร้างผู้ใช้ได้หากยังไม่มีข้อมูลประจำตัวเหล่านั้น นอกจากนี้ยังตรวจสอบสิทธิ์ได้อย่างง่ายดายด้วยโปรไฟล์โซเชียลจาก Google Play Games, Facebook, Game Center ฯลฯ
var email = "[email protected]"
var password = "batsignal"
# Use 'await' to wait for the request to complete.
var session : NakamaSession = await client . authenticate_email_async ( email , password )
print ( session )
เมื่อตรวจสอบสิทธิ์แล้ว เซิร์ฟเวอร์จะตอบสนองด้วยโทเค็นการรับรองความถูกต้อง (JWT) ซึ่งมีคุณสมบัติที่เป็นประโยชน์และได้รับการดีซีเรียลไลซ์ลงในอ็อบเจ็กต์ NakamaSession
print ( session . token ) # raw JWT token
print ( session . user_id )
print ( session . username )
print ( "Session has expired: %s " % session . expired )
print ( "Session expires at: %s " % session . expire_time )
ขอแนะนำให้จัดเก็บโทเค็นการตรวจสอบสิทธิ์จากเซสชันและตรวจสอบเมื่อเริ่มต้นระบบว่าหมดอายุแล้วหรือไม่ หากโทเค็นหมดอายุ คุณต้องตรวจสอบสิทธิ์อีกครั้ง เวลาหมดอายุของโทเค็นสามารถเปลี่ยนแปลงได้ตามการตั้งค่าในเซิร์ฟเวอร์
var authtoken = "restored from somewhere"
var session2 = NakamaClient . restore_session ( authtoken )
if session2 . expired :
print ( "Session has expired. Must reauthenticate!" )
หมายเหตุ: ความยาวอายุการใช้งานของเซสชันสามารถเปลี่ยนแปลงได้บนเซิร์ฟเวอร์ด้วยอาร์กิวเมนต์แฟล็กคำสั่ง --session.token_expiry_sec
ไคลเอนต์มี API ในตัวมากมายสำหรับคุณสมบัติต่าง ๆ ของเซิร์ฟเวอร์เกม สิ่งเหล่านี้สามารถเข้าถึงได้ด้วยวิธีอะซิงก์ นอกจากนี้ยังสามารถเรียกตรรกะแบบกำหนดเองในฟังก์ชัน RPC บนเซิร์ฟเวอร์ได้อีกด้วย สิ่งเหล่านี้สามารถดำเนินการได้ด้วยวัตถุซ็อกเก็ต
คำขอทั้งหมดจะถูกส่งไปพร้อมกับวัตถุเซสชันที่อนุญาตไคลเอ็นต์
var account = await client . get_account_async ( session )
print ( account . user . id )
print ( account . user . username )
print ( account . wallet )
เนื่องจาก Godot Engine ไม่รองรับข้อยกเว้น เมื่อใดก็ตามที่คุณส่งคำขอแบบอะซิงก์ผ่านไคลเอนต์หรือซ็อกเก็ต คุณจึงสามารถตรวจสอบได้ว่ามีข้อผิดพลาดเกิดขึ้นหรือไม่โดยใช้เมธอด is_exception()
var an_invalid_session = NakamaSession . new () # An empty session, which will cause and error when we use it.
var account2 = await client . get_account_async ( an_invalid_session )
print ( account2 ) # This will print the exception
if account2 . is_exception ():
print ( "We got an exception" )
ไคลเอนต์สามารถสร้างซ็อกเก็ตอย่างน้อยหนึ่งซ็อกเก็ตกับเซิร์ฟเวอร์ แต่ละซ็อกเก็ตสามารถมีผู้ฟังเหตุการณ์ของตัวเองที่ลงทะเบียนเพื่อรับการตอบสนองจากเซิร์ฟเวอร์
var socket = Nakama . create_socket_from ( client )
socket . connected . connect ( self . _on_socket_connected )
socket . closed . connect ( self . _on_socket_closed )
socket . received_error . connect ( self . _on_socket_error )
await socket . connect_async ( session )
print ( "Done" )
func _on_socket_connected ():
print ( "Socket connected." )
func _on_socket_closed ():
print ( "Socket closed." )
func _on_socket_error ( err ):
printerr ( "Socket error %s " % err )
Godot จัดเตรียม High-level Multiplayer API ซึ่งช่วยให้นักพัฒนาสามารถสร้าง RPC เรียกใช้ฟังก์ชันที่ทำงานบนเพื่อนคนอื่นๆ ในการแข่งขันแบบผู้เล่นหลายคน
ตัวอย่างเช่น:
func _process ( delta ):
if not is_multiplayer_authority ():
return
var input_vector = get_input_vector ()
# Move the player locally.
velocity = input_vector * SPEED
move_and_slide ()
# Then update the player's position on all other connected clients.
update_remote_position . rpc ( position )
@rpc ( any_peer )
func update_remote_position ( new_position ):
position = new_position
Godot มีแบ็กเอนด์ในตัวจำนวนหนึ่งสำหรับการส่ง RPC รวมถึง: ENet, WebSockets และ WebRTC
อย่างไรก็ตาม คุณยังสามารถใช้ไคลเอนต์ Nakama เป็นแบ็กเอนด์ได้! ซึ่งจะทำให้คุณสามารถใช้ High-level Multiplayer API ที่คุ้นเคยของ Godot ต่อไปได้ แต่ด้วย RPC ที่ส่งผ่านการแข่งขัน Nakama แบบเรียลไทม์อย่างโปร่งใส
หากต้องการทำเช่นนั้น คุณต้องใช้คลาส NakamaMultiplayerBridge
:
var multiplayer_bridge
func _ready ():
# [...]
# You must have a working 'socket', created as described above.
multiplayer_bridge = NakamaMultiplayerBridge . new ( socket )
multiplayer_bridge . match_join_error . connect ( self . _on_match_join_error )
multiplayer_bridge . match_joined . connect ( self . _on_match_joined )
get_tree (). get_multiplayer (). set_multiplayer_peer ( multiplayer_bridge . multiplayer_peer )
func _on_match_join_error ( error ):
print ( "Unable to join match: " , error . message )
func _on_match_join () -> void :
print ( "Joined match with id: " , multiplayer_bridge . match_id )
คุณยังสามารถเชื่อมต่อกับสัญญาณปกติใดๆ บน MultiplayerAPI
ได้ เช่น:
get_tree (). get_multiplayer (). peer_connected . connect ( self . _on_peer_connected )
get_tree (). get_multiplayer (). peer_disconnected . connect ( self . _on_peer_disconnected )
func _on_peer_connected ( peer_id ):
print ( "Peer joined match: " , peer_id )
func _on_peer_disconnected ( peer_id ):
print ( "Peer left match: " , peer_id )
จากนั้นคุณจะต้องเข้าร่วมการแข่งขันโดยใช้วิธีใดวิธีหนึ่งต่อไปนี้:
สร้างการแข่งขันส่วนตัวใหม่โดยมีลูกค้าของคุณเป็นเจ้าภาพ
multiplayer_bridge . create_match ()
เข้าร่วมการแข่งขันส่วนตัว
multiplayer_bridge . join_match ( match_id )
สร้างหรือเข้าร่วมการแข่งขันส่วนตัวด้วยชื่อที่กำหนด
multiplayer_bridge . join_named_match ( match_name )
ใช้ระบบจับคู่เพื่อค้นหาและเข้าร่วมการแข่งขันสาธารณะ
var ticket = await socket . add_matchmaker_async ()
if ticket . is_exception ():
print ( "Error joining matchmaking pool: " , ticket . get_exception (). message )
return
multiplayer_bridge . start_matchmaking ( ticket )
หลังจากส่งสัญญาณ "match_joined" แล้ว คุณสามารถเริ่มส่ง RPC ได้ตามปกติด้วยฟังก์ชัน rpc()
และเรียกใช้ฟังก์ชันอื่นๆ ที่เกี่ยวข้องกับ High-level Multiplayer API เช่น get_tree().get_multiplayer().get_unique_id()
และ node.set_network_authority(peer_id)
และ node.is_network_authority()
หากคุณใช้ Godot เวอร์ชัน .NET พร้อมรองรับ C# คุณสามารถใช้ไคลเอนต์ Nakama .NET ซึ่งสามารถติดตั้งผ่าน NuGet ได้:
dotnet add package NakamaClient
Addon นี้มีคลาส C# บางคลาสสำหรับใช้กับไคลเอนต์ .NET เพื่อให้มีการบูรณาการกับ Godot ได้ลึกยิ่งขึ้น:
GodotLogger
: เครื่องบันทึกที่พิมพ์ไปยังคอนโซล GodotGodotHttpAdapter
: อะแดปเตอร์ HTTP ที่ใช้โหนด HTTPRequest ของ GodotGodotWebSocketAdapter
: อะแดปเตอร์ซ็อกเก็ตที่ใช้ WebSocketClient ของ Godotนี่คือตัวอย่างวิธีใช้งาน:
var http_adapter = new GodotHttpAdapter ( ) ;
// It's a Node, so it needs to be added to the scene tree.
// Consider putting this in an autoload singleton so it won't go away unexpectedly.
AddChild ( http_adapter ) ;
const string scheme = "http" ;
const string host = "127.0.0.1" ;
const int port = 7350 ;
const string serverKey = "defaultkey" ;
// Pass in the 'http_adapter' as the last argument.
var client = new Client ( scheme , host , port , serverKey , http_adapter ) ;
// To log DEBUG messages to the Godot console.
client . Logger = new GodotLogger ( "Nakama" , GodotLogger . LogLevel . DEBUG ) ;
ISession session ;
try {
session = await client . AuthenticateDeviceAsync ( OS . GetUniqueId ( ) , "TestUser" , true ) ;
}
catch ( ApiResponseException e ) {
GD . PrintErr ( e . ToString ( ) ) ;
return ;
}
var websocket_adapter = new GodotWebSocketAdapter ( ) ;
// Like the HTTP adapter, it's a Node, so it needs to be added to the scene tree.
// Consider putting this in an autoload singleton so it won't go away unexpectedly.
AddChild ( websocket_adapter ) ;
// Pass in the 'websocket_adapter' as the last argument.
var socket = Socket . From ( client , websocket_adapter ) ;
หมายเหตุ: ไคลเอนต์ Nakama .NET ที่ใช้งานได้ทันทีจะทำงานได้ดีกับเดสก์ท็อปที่สร้างเกมของคุณ! อย่างไรก็ตาม มันจะใช้งานไม่ได้กับบิลด์ HTML5 เว้นแต่คุณจะใช้คลาส GodotHttpAdapter
และ GodotWebSocketAdapter
Satori เป็นเซิร์ฟเวอร์ liveops สำหรับเกมที่ขับเคลื่อนการวิเคราะห์ที่ดำเนินการได้ การทดสอบ A/B และการกำหนดค่าระยะไกล ใช้ Satori Godot Client เพื่อสื่อสารกับ Satori จากภายในเกม Godot ของคุณ
Satori เข้ากันได้กับ Godot 4 เท่านั้น
เอกสารฉบับเต็มออนไลน์อยู่ - https://heroiclabs.com/docs/satori/client-libraries/godot/index.html
เพิ่ม Satori.gd
singleton (ใน addons/com.heroiclabs.nakama/
) เป็นการโหลดอัตโนมัติใน Godot
สร้างออบเจ็กต์ไคลเอนต์ที่ยอมรับคีย์ API ที่คุณได้รับในฐานะลูกค้า Satori
extends Node
func ready ():
var scheme = "http"
var host = "127.0.0.1"
var port : Int = 7450
var apiKey = "apiKey"
var client := Satori . create_client ( apiKey , host , port , scheme )
จากนั้นตรวจสอบสิทธิ์กับเซิร์ฟเวอร์เพื่อรับเซสชันของคุณ
// Authenticate with the Satori server .
var session = await _client . authenticate_async ( "your-id" )
if session . is_exception ():
print ( "Error authenticating: " + session . get_exception (). _message )
else :
print ( "Authenticated successfully." )
เมื่อใช้ไคลเอนต์ คุณสามารถรับการทดลองหรือแฟล็กฟีเจอร์ที่ผู้ใช้เป็นเจ้าของ
var experiments = await _client . get_experiments_async ( session , [ "experiment1" , "Experiment2" ])
var flag = await _client . get_flag_async ( session , "FlagName" )
คุณยังสามารถส่งเหตุการณ์ที่กำหนดเองไปยังเซิร์ฟเวอร์ได้:
var _event = Event . new ( "gameFinished" , Time . get_unix_time_from_system ())
await _client . event_async ( session , _event )
แผนงานการพัฒนาได้รับการจัดการตามปัญหา GitHub และยินดีรับคำขอดึง หากคุณสนใจที่จะปรับปรุงโค้ด โปรดเปิดประเด็นเพื่อหารือเกี่ยวกับการเปลี่ยนแปลงหรือแวะเข้ามาและหารือเกี่ยวกับเรื่องนี้ในฟอรัมชุมชน
หากต้องการรันการทดสอบ คุณจะต้องรันเซิร์ฟเวอร์และฐานข้อมูล การทดสอบส่วนใหญ่เขียนเป็นการทดสอบการรวมซึ่งดำเนินการกับเซิร์ฟเวอร์ วิธีที่รวดเร็วที่เราใช้กับเวิร์กโฟลว์ทดสอบของเราคือการใช้ไฟล์เขียน Docker ที่อธิบายไว้ในเอกสารประกอบ
นอกจากนี้ คุณจะต้องคัดลอก (หรือ symlink) โฟลเดอร์ addons
ภายในโฟลเดอร์ test_suite
ตอนนี้คุณสามารถรันโปรเจ็กต์ test_suite
ได้จาก Godot Editor
หากต้องการรันการทดสอบบนเครื่องที่ไม่มีส่วนหัว (ไม่มี GPU) คุณสามารถดาวน์โหลดสำเนาของ Godot Headless และเรียกใช้จากบรรทัดคำสั่ง
หากต้องการทำให้ขั้นตอนนี้เป็นแบบอัตโนมัติ ให้ย้ายไบนารี headless ไปที่ test_suite/bin/godot.elf
และรันการทดสอบผ่านเชลล์สคริปต์ test_suite/run_tests.sh
(โค้ดออกจะรายงานความล้มเหลว/ความสำเร็จของการทดสอบ)
cd nakama
docker-compose -f ./docker-compose-postgres.yml up
cd ..
cd nakama-godot
sh test_suite/run_tests.sh
หากต้องการเตรียมรุ่นใหม่ให้พร้อมสำหรับการเผยแพร่ เพียงซิปโฟลเดอร์ addons ซ้ำๆ (อาจเพิ่ม CHANGELOG
, LICENSE
และ README.md
ด้วย)
บนระบบยูนิกซ์ คุณสามารถรันคำสั่งต่อไปนี้ (แทนที่ $VERSION
ด้วยหมายเลขเวอร์ชันที่ต้องการ) อย่าลืมอัปเดตไฟล์ CHANGELOG
ก่อน
zip -r nakama- $VERSION .zip addons/ LICENSE CHANGELOG.md README.md
โครงการนี้ได้รับอนุญาตภายใต้ใบอนุญาต Apache-2