現在我們已經可以完整的同步不同玩家的資訊,不過依舊無法將玩家的狀態保存在伺服器。因此我們需要改寫伺服器來保存狀態,受限於內容長度以及 RPG Maker MV 的限制,我們會直接使用輸入使用者 ID 的方式來做使用者認證,在實際的應用中建議考慮像是 Unlight 選用的 SRP 這類機制來加強安全性。
RPG Maker 因為沒有實作輸入框(要在 Canvas 上實作也很困難)因此不太能在很短的篇幅內快速搭建一個跟遊戲畫面相融合的輸入介面,作為替代我們使用簡單的
prompt
透過瀏覽器內建的方式快速跳出選項,就會相對容易許多。
管理資料庫的變動是一件複雜的事情,我們雖然可以透過 SQL 維護。但是沒有專業 DBA(資料庫管理者)的情況下並不容易,以及開發專案時還是需要能建構開發環境,因此採用 Migration 的方式對中小型的專案會是一個不錯的選擇。
首先,我們先對 config/application.rb
擴充,加上資料庫的設定。
# config/application.rb
# frozen_string_literal: true
# ...
class SimpleRPG
include Singleton
class << self
# ...
def database_url
ENV['DATABASE_URL'] ||
"sqlite://#{root}/db/development.sqlite"
end
def database
@database ||=
Sequel.connect(database_url)
end
end
# ...
def start(server, options = {})
SimpleRPG.database
Thread.new { @fsevent.run }
# TODO: Select Server
Rack::Handler.default.run(WebSocketServer, options)
end
end
Sequel 在設定上算是很容易的,只需要在要使用資料庫的時候先呼叫一次 Sequel 的 #connect
方法,就可以連上資料庫。這次因為是開發與示範使用,所以就直接讓他在專案目錄下的 db/
產生一個 development.sqlite
檔案。
實際專案使用的時候可以參考 Rails 製作一個
SimpleRPG.env
的設定值,透過判斷目前的環境變數來決定產生的檔案,或者讀取專用的設定檔來設定要連上的資料庫。
在 Unlight 使用的舊版 Sequel 中是在 Model 直接定義 Schema 的,也因此會讓 Model 增加很多程式碼。現在比較新版本的 Sequel 5 已經出現「棄用警告」通知我們應該改成使用單獨的 Migration 檔案來處理,不過預設不會載入這個功能而且我們應該只在需要更新時運行這些更新指令。
因此先新增一個 bin/migrate
執行檔,用 Ruby 來啟動這個資料庫更新的腳本。
# bin/migrate
#!/usr/bin/env ruby
# frozen_string_literal: true
$LOAD_PATH.unshift("#{File.absolute_path('../..', __FILE__)}")
require 'logger'
require 'config/application'
Sequel.extension :migration
Sequel.connect(SimpleRPG.database_url, logger: Logger.new($stderr)) do |db|
Sequel::Migrator.run(
db,
"#{SimpleRPG.root}/db/migrations",
use_transactions: true
)
end
實作上跟啟動伺服器的方式差不多,這邊我們需要將 Logger
設定為輸出到 $stderr
上這樣才能正確地看到錯誤訊息,如果是 $stdout
的話還是會無法看到(可能是 Sequel 的設計關係,有點微妙)為了確保更新失敗後能正確的 Rollback 因此將 use_transactions
模式啟用,這部分可以自行評估設計的系統是否需要這樣的機制。
完成後一樣需要對 bin/migrate
這個檔案做 chmod +x bin/migrate
的設定,確保能夠執行。
在前面的步驟我們設定了 db/migrations
作為讀取 Migration 的目錄,接下來要進行撰寫的設定。在 Sequel 中檔案有兩種命名方式,一種是用序號命名另一種則是用日期跟時間。以經驗上來看用日期命名會比用序號好的多,因此我們編輯一個 201910141700_create_players.rb
的檔案來產生 players
這張表儲存玩家資訊。
# db/migrations/201910141700_create_players.rb
# frozen_string_literal: true
Sequel.migration do
change do
create_table :players do
primary_key :id
String :name, null: false, index: true, unique: true
Integer :x, default: 0
Integer :y, default: 0
end
end
end
關於如何定義 Scheme 的部分需要參考 Sequel 的 文件,因為跟大多數 Ruby 開發者熟悉的 ActiveRecord 有所差異,因此建議先參考文件了解要如何設定後再開始撰寫會比較好。
當我們產生好對應的檔案後,就可以執行 bin/migrate
來確認是否能正常執行。
[elct9620] server % ./bin/migrate
I, [2019-10-13T23:47:10.887144 #20938] INFO -- : (0.001378s) PRAGMA foreign_keys = 1
I, [2019-10-13T23:47:10.887241 #20938] INFO -- : (0.000018s) PRAGMA case_sensitive_like = 1
I, [2019-10-13T23:47:10.887598 #20938] INFO -- : (0.000077s) SELECT sqlite_version()
E, [2019-10-13T23:47:10.887768 #20938] ERROR -- : SQLite3::SQLException: no such table: schema_migrations: SELECT NULL AS 'nil' FROM `schema_migrations` LIMIT 1
I, [2019-10-13T23:47:10.891970 #20938] INFO -- : (0.001127s) CREATE TABLE `schema_migrations` (`filename` varchar(255) NOT NULL PRIMARY KEY)
E, [2019-10-13T23:47:10.892312 #20938] ERROR -- : SQLite3::SQLException: no such table: schema_info: SELECT NULL AS 'nil' FROM `schema_info` LIMIT 1
I, [2019-10-13T23:47:10.892590 #20938] INFO -- : (0.000077s) SELECT `filename` FROM `schema_migrations` ORDER BY `filename`
I, [2019-10-13T23:47:10.892903 #20938] INFO -- : Begin applying migration 201910141700_create_players.rb, direction: up
I, [2019-10-13T23:47:10.892986 #20938] INFO -- : (0.000019s) BEGIN
I, [2019-10-13T23:47:10.893436 #20938] INFO -- : (0.000270s) CREATE TABLE `players` (`id` integer NOT NULL PRIMARY KEY AUTOINCREMENT, `name` varchar(255) NOT NULL UNIQUE, `x` integer DEFAULT (0), `y` integer DEFAULT (0))
I, [2019-10-13T23:47:10.893597 #20938] INFO -- : (0.000071s) CREATE INDEX `players_name_index` ON `players` (`name`)
I, [2019-10-13T23:47:10.893769 #20938] INFO -- : (0.000081s) INSERT INTO `schema_migrations` (`filename`) VALUES ('201910141700_create_players.rb')
I, [2019-10-13T23:47:10.894314 #20938] INFO -- : (0.000485s) COMMIT
I, [2019-10-13T23:47:10.894376 #20938] INFO -- : Finished applying migration 201910141700_create_players.rb, direction: up, took 0.001465 seconds
如果看到類似上述的訊息,基本上就算是執行成功。並且要確定再次執行 bin/migrate
時不會看到相同的結果,因為已經執行過同樣的 Migration 了!
建議在
db/
目錄下放置.gitkeep
檔案才不會因為找不到目錄而無法產生 SQLite 檔案,也可以使用sqlite3
指令檢查產生的資料庫檔案是否有正確的生成我們所需要的players
表。
我的個人部落格是弦而時習之平常會把自己發現的一些新技巧紀錄在上面,也歡迎大家來逛逛。