一、執行效果:
1、正確配置服務並訪問頁面後:
world、a、b三個使用者存在於同一場景中(在實際應用時應該分別使用不同的顯示裝置),a、b是普通使用者,在場景中用一個球體表示,world是裁判使用者,不在場景中顯示實體。場景中心有一副撲克牌,裁判使用者能夠看到牌面,而普通使用者只能看到卡背。每個使用者可使用WASD、空格、Shift和滑鼠控制自身的移動,其位置和姿態變化能夠被其他使用者實時感知。
2、world使用者測試物理引擎:
world使用者點選滑鼠左鍵可以向游標方向發射具有物理效果的小方塊,小方塊在接觸到場景邊界或其他物理物件時將發生碰撞。
3、抓牌同步
普通使用者(這裡以a為例),點選牌堆時將從牌堆抓取卡牌,其他使用者將能實時圍觀這一動作,卡牌到達a的手中後將顯示卡面。
4、出牌同步
a使用者按一下Alt鍵切換游標控制模式,點選一張手牌,被選中的手牌將顯示橙色邊框,再按Alt鍵則準星變為橙色,在橙色準星狀態下點選左鍵,將帶有物理效果的紅桃2打出(為了顯示的更明顯加大了卡片的反彈係數)
5、整體場景
二、服務端部署與前端訪問方法:
1、從https://github.com/ljzc002/CardSimulate或https://down.51cto.com/data/2461249下載工程
2、下載安裝h2資料庫軟體,將playground.mv.db和playground.trace.db放入h2資料夾的同級目錄,複製完畢後啟動資料庫。這個資料庫中儲存了初始的使用者身份資訊(TB_USER)、卡牌屬性資訊(TB_UINTTYPE)和使用者掌控卡牌資訊(TB_USERUNIT)。
如我的h2資料庫最外層目錄為h2-2017-06-10,其內為h2資料夾的同級目錄。
這個資料庫的JDBC驅動類為org.h2.Driver,URL為jdbc:h2:tcp://127.0.0.1/../../playground,使用者名稱密碼均為playground。
3、首先確保系統支援64位JDK1.8。將從Github下載的程式碼放入已有的SpringBoot2工程,或者用IntelliJ IDEA 開啟從51CTO下載的完整工程(其中前端程式碼並非最新,可從Github替換),等待IntelliJ IDEA根據pom.xml自動安裝依賴,匯入完成後的工程結構如下圖:
這裡我使用SpringBoot進行包管理,用Netty實現WebSocket功能,用SpringBoot的內建Tomcat處理http請求,用druid作為資料來源,讀者也可以考慮用其他方式實現這些功能。
4、埠配置
這裡預設使用8181作為http監聽埠,用2323作為WebSocket監聽埠,可以在application.yml檔案中修改後端監聽配置,可在login.html和KingoftheHill2.html中修改前端的預設訪問埠。
5、打包和啟動
在playground目錄下執行mvn clean package或執行package.bat將工程打包為一個jar包。
執行java -jar ./target/netty_test01.jar或startjar.bat啟動剛才生成的jar包(這個jar包包括了執行所需的所有依賴,可以剪下到其他目錄啟動)
出現“Netty開始監聽:2323”,且無報錯資訊說明服務啟動成功。
6、在與伺服器處於同一網段的裝置上,用Chrome瀏覽器訪問http://127.0.0.1:8181/HTML/login.html(IP和埠號要根據實際情況修改),開啟使用者登入頁面。
輸入使用者名稱密碼,如果之前不存在此使用者則以此使用者名稱密碼新建使用者,如果已存在此使用者名稱則嘗試以此密碼登入。
為了方便操作,這裡使用非常寬鬆的身份驗證規則,一次登入成功後即在瀏覽器本地儲存中建立一個不會過期的token,憑此token這個瀏覽器將可以不受限制的隨時訪問全部前後端資源,一個瀏覽器也可以同時儲存多個使用者的token,以支援一機多開。特別強調此種身份驗證不適用於需要保密的場合。
這裡認為本系統的使用者之間相互熟悉、相互信任,且假設使用者之間能夠及時高效的進行線下溝通,身份驗證只是為了方便多使用者的合作分工而設。
需要注意的是瀏覽器對每個IP和埠都建立獨立的本地儲存,所以更換IP和埠後需要重新登入,但原使用者的資訊(比如控制的單位)仍然不變(儲存在資料庫中)。
7、導航頁面:
這是一個參考時下流行的資訊系統導航頁結構,用原生JavaScript編寫的導航頁面,登入時填寫的資訊以window.location.hash的形式傳遞進來,即使關閉此導航頁,使用者也將一直在資料庫中保持啟用狀態,隨時可以再次連線,直到點選右上角的“退出登入”按鈕,使用者才會在資料庫中被置為不可用。
導航選單欄內容在Index.js的InitMenu方法中定義:
1 var arr_user = [ 2 ["測試選單","./PAGE/chat.html","bg/pic_test","test","unique"], 3 ["WS初始化測試","./PAGE/KingoftheHill2.html","bg/pic_test","test","unique"], 4 ["在新視窗中開啟","./PAGE/OpenWindow.html" 5 ,"bg/pic_test","test","unique"] 6 ];
陣列中的引數分別表示選單名、選單對應url、選單所屬選單組的圖示、選單所屬的選單組、這種選單隻能開啟一個,在實際使用時可以考慮從資料庫按使用者許可權進行選單載入。
8、點選“在新視窗中開啟”選單,開啟真正的WebSocket頁面,這裡使用world使用者進行所有的物理引擎計算和動畫計算,普通使用者只向world提交操作請求以及渲染world的計算結果,所以在執行時需要先開啟world使用者的WebSocket頁,再開啟其他使用者的WebSocket頁(但登入順序沒有要求)。
在已經具有token的情況下,也可以不經過導航頁,直接輸入http://127.0.0.1:8181/HTML/PAGE/KingoftheHill2.html#127.0.0.1@8282@2323@world訪問
三、工程結構簡介
因為涉及到的程式碼較多,無法一一展開介紹,這裡只簡單說明一些目錄和檔案的作用,具體的執行方式可以參考我之前的部落格和網上的各種資料。
1、main.java.com下是後端的SpringBoot Java程式碼:其中appwithmain是一個以swt技術編寫的Java桌面程式,用來處理另一個專案中cnpm的包安裝異常,和本專案無關。learnnetty是從網上摘抄的一些netty學習例程,和本專案無關。websocket下是與本專案有關的後端程式碼:
其中HttpController2下是http請求的處理方法,主要包括使用者身份驗證和使用者登出功能。
netty中的NettyConfig、serverBootStrap、serverInitializer、WebSocketServer根據網上的教程修改而來,因為自身對SpringBoot和Netty瞭解有限,只是知其然不知其所以然的生搬硬套,這裡的程式碼肯定存在各種缺陷,讀者可以考慮用更合適的方式代替。
WebSocketHandler是WebSocket請求的實際處理者,它的主要工作一是根據前臺發來的token和資料庫資訊對WebSocket請求進行身份驗證,二是根據請求中type屬性的不同將資訊轉發給不同的物件。
pojo目錄中只用到了WsSession類,它負責在Java層儲存一個WebSocket連線的身份資訊:
1 public class WsSession { 2 public String ChannelId=""; 3 public String UserId=""; 4 public String Token=""; 5 }
這裡Token是使用者前後臺通用的唯一標識,與資料庫tb_user表的uuid欄位對應;UserId是使用者的顯示名,它與Token可能是一對多的關係,比如“張三”使用者登入後選擇退出登入,則會在資料庫的tb_user表中留下一條sfky(是否可用)為0的記錄,當有使用者再次以張三為名登入時,資料庫中將有兩條userid為張三但uuid各不相同的記錄。
ChannelId則是這一條WebSocket連線的唯一id,考慮到未來程式功能的擴充套件,一個userid可能同時具有多個channelld。
MyNettyUtil裡是一些輔助工具方法。
PlaygroundApplication是SpringBoot的入口類,因為不熟悉SpringBoot的各種約定,在這裡手動New了一個資料來源給Netty模組使用。
2、main.resources.static目錄下是前端程式碼:
其結構和前面部落格中的一些Babylon.js例程並沒有太大區別,
其中HTML.PAGE下的KingoftheHill2.html是本工程的主要Babylon.js頁面,其他多為各種功能的測試頁面。
JS.PAGE.kh下的khws.js是新加入的js檔案,包含所有處理收到的WebSocket資訊的邏輯。
3、pom.xml是Maven的配置檔案,其後半部分打包配置取自https://blog.csdn.net/wuqingbin/article/details/80340110,在此對原作者表示感謝。
4、本工程中的自編程式碼以MIT協議釋出,引用的程式碼以其原本協議使用。