日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > java >内容正文

java

Java解析魔兽争霸3录像W3G文件(四):解析游戏进行时的信息

發(fā)布時(shí)間:2024/8/1 java 51 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java解析魔兽争霸3录像W3G文件(四):解析游戏进行时的信息 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

在上一篇博文中,通過(guò)解析壓縮數(shù)據(jù)塊解壓縮后的數(shù)據(jù)的前一部分,可以獲取到游戲開始前的一些信息,緊接著游戲開始前的信息之后,就是游戲進(jìn)行時(shí)的信息了,其中包括玩家游戲中的操作,例如造建筑,出兵,攻擊,移動(dòng)等,還包括玩家游戲中的聊天信息,玩家退出游戲等。

游戲進(jìn)行時(shí)的信息由很多個(gè)數(shù)據(jù)塊組成。這些數(shù)據(jù)塊有幾種類型,每個(gè)數(shù)據(jù)塊的第一個(gè)字節(jié)就是數(shù)據(jù)塊ID,用于標(biāo)識(shí)數(shù)據(jù)塊的類型。

下面列出各種數(shù)據(jù)塊類型及其對(duì)應(yīng)的數(shù)據(jù)塊ID、數(shù)據(jù)塊字節(jié)數(shù)和數(shù)據(jù)塊結(jié)構(gòu):

1、0x17 - 玩家離開游戲的數(shù)據(jù)塊
數(shù)據(jù)塊ID:0x17
數(shù)據(jù)塊字節(jié)數(shù):14字節(jié)
結(jié)構(gòu):
1字節(jié):數(shù)據(jù)塊ID,0x17;
2~5字節(jié):原因;
6字節(jié):玩家ID;
7~10字節(jié):結(jié)果;
11~14字節(jié):未知。

2、0x20 - 玩家聊天信息的數(shù)據(jù)塊
數(shù)據(jù)塊ID:0x20
數(shù)據(jù)塊字節(jié)數(shù):n + 4字節(jié)
結(jié)構(gòu):
1字節(jié):數(shù)據(jù)塊ID,0x20;
2字節(jié):玩家ID;
3~4字節(jié):數(shù)據(jù)塊剩余的數(shù)據(jù)的字節(jié)數(shù)n;
5字節(jié):flag;
6~9字節(jié):聊天模式,0x00:對(duì)所有玩家,0x01:對(duì)隊(duì)友,0x02:對(duì)裁判或觀看者,0x03或大于0x03:對(duì)指定玩家,玩家的slotNumber是該值減去3的結(jié)果,注意這里是slotNumber而不是玩家ID;
10~n+4字節(jié):聊天內(nèi)容,字符串,最后一個(gè)字節(jié)是0x00。

3、0x1E/0x1F – 游戲時(shí)間段(TimeSlot)數(shù)據(jù)塊
數(shù)據(jù)塊ID:0x1E或0x1F
數(shù)據(jù)塊字節(jié)數(shù):n+3字節(jié)
結(jié)構(gòu):
1字節(jié):數(shù)據(jù)塊ID,0x1E或0x1F;
2~3字節(jié):數(shù)據(jù)塊剩余的數(shù)據(jù)的字節(jié)數(shù)n,最小值可能是2;
4~5字節(jié):時(shí)間段的時(shí)間長(zhǎng)度(毫秒,值一般是100毫秒左右);
6~n+3字節(jié):玩家在這個(gè)時(shí)間段內(nèi)的操作信息,當(dāng)n為2時(shí)這部分不存在。這部分內(nèi)容在下面一篇博文中再進(jìn)行解析。

以上三種類型的數(shù)據(jù)塊是需要解析的數(shù)據(jù)塊,下面還有幾種類型的數(shù)據(jù)塊就不用去解析:

4、0x1A/0x1B/0x1C,5字節(jié);
5、0x22,6字節(jié);
6、0x23,11字節(jié);
7、0x2F,9字節(jié)。

其中,游戲時(shí)間段(TimeSlot)數(shù)據(jù)塊是游戲時(shí)間進(jìn)度的標(biāo)識(shí),每個(gè)時(shí)間段100毫秒左右,其中包含玩家在這段時(shí)間內(nèi)的操作信息。而游戲時(shí)間段數(shù)據(jù)塊中的毫秒數(shù)累加后,就是游戲進(jìn)行到的時(shí)間。比如玩家的操作、聊天、離開游戲的時(shí)間,就可以用其對(duì)應(yīng)數(shù)據(jù)塊之前的所有TimeSlot數(shù)據(jù)塊中的時(shí)間累加來(lái)計(jì)算。

Java解析游戲進(jìn)行時(shí)的信息:

添加ReplayData.java用來(lái)解析游戲進(jìn)行時(shí)的信息。

ReplayData.java

package com.xxg.w3gparser;import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.List;public class ReplayData {/*** 解壓縮的字節(jié)數(shù)組*/private byte[] uncompressedDataBytes;/*** 解析的字節(jié)位置*/private int offset;/*** 玩家列表*/private List<Player> playerList;/*** 游戲進(jìn)行時(shí)的時(shí)間(毫秒)*/private long time;/*** 聊天信息集合*/private List<ChatMessage> chatList = new ArrayList<ChatMessage>();public ReplayData(byte[] uncompressedDataBytes, int offset, List<Player> playerList) throws W3GException, UnsupportedEncodingException {this.uncompressedDataBytes = uncompressedDataBytes;this.offset = offset;this.playerList = playerList;analysis();}/*** 解析*/private void analysis() throws UnsupportedEncodingException, W3GException{byte blockId = 0;while ((blockId = uncompressedDataBytes[offset]) != 0) {switch (blockId) {// 聊天信息case 0x20:analysisChatMessage();break;// 時(shí)間段(一般是100毫秒左右一段)case 0x1E:case 0x1F:analysisTimeSlot();break;// 玩家離開游戲case 0x17:analysisLeaveGame();break;// 未知的BlockIdcase 0x1A:case 0x1B:case 0x1C: offset += 5;break;case 0x22:offset += 6;break;case 0x23:offset += 11;break;case 0x2F:offset += 9;break;// 無(wú)效的Blockdefault:throw new W3GException("無(wú)效Block,ID:" + blockId);}}}/*** 解析聊天信息*/private void analysisChatMessage() throws UnsupportedEncodingException {ChatMessage chatMessage = new ChatMessage();offset++;byte playerId = uncompressedDataBytes[offset];chatMessage.setFrom(getPlayById(playerId));offset++;int bytes = LittleEndianTool.getUnsignedInt16(uncompressedDataBytes, offset);offset += 2;offset++;long mode = LittleEndianTool.getUnsignedInt32(uncompressedDataBytes, offset);if(mode >= 3) {int receiverPlayerId = (int) (mode - 3);chatMessage.setTo(getPlayBySlotNumber(receiverPlayerId));}chatMessage.setMode(mode);offset += 4;String message = new String(uncompressedDataBytes, offset, bytes - 6, "UTF-8");chatMessage.setMessage(message);offset += bytes - 5;chatMessage.setTime(time);chatList.add(chatMessage);}/*** 解析一個(gè)時(shí)間塊*/private void analysisTimeSlot() {offset++;int bytes = LittleEndianTool.getUnsignedInt16(uncompressedDataBytes, offset);offset += 2;int timeIncrement = LittleEndianTool.getUnsignedInt16(uncompressedDataBytes, offset);time += timeIncrement;offset += 2;offset += bytes - 2;}/*** 玩家離開游戲Block解析*/private void analysisLeaveGame() {offset += 5;// 玩家離開游戲就不再計(jì)算游戲時(shí)間byte playerId = uncompressedDataBytes[offset];Player player = getPlayById(playerId);player.setPlayTime(time);offset += 9;}/*** 通過(guò)玩家ID獲取Player對(duì)象* @param playerId 玩家ID* @return 對(duì)應(yīng)的Player對(duì)象*/private Player getPlayById(byte playerId) {Player p = null;for(Player player : playerList) {if(playerId == player.getPlayerId()) {p = player;break;}}return p;}/*** 通過(guò)玩家SlotNumber獲取Player對(duì)象* @param slotNumber 玩家SlotNumber* @return 對(duì)應(yīng)的Player對(duì)象*/private Player getPlayBySlotNumber(int slotNumber) {Player p = null;for(Player player : playerList) {if(slotNumber == player.getSlotNumber()) {p = player;break;}}return p;}public List<ChatMessage> getChatList() {return chatList;}}
ChatMessage.java是玩家游戲過(guò)程中的聊天信息對(duì)應(yīng)的Java對(duì)象,通過(guò)解析玩家聊天信息的數(shù)據(jù)塊獲取。

ChatMessage.java

package com.xxg.w3gparser;public class ChatMessage {/*** 發(fā)送者*/private Player from;/*** 發(fā)送方式* 0:發(fā)送給所有玩家* 1:發(fā)送給隊(duì)友* 2:發(fā)送給裁判或觀看者* 3+N:發(fā)送給指定玩家*/private long mode;/*** 接收者(mode為3+N時(shí)有效)*/private Player to;/*** 消息發(fā)送時(shí)間*/private long time;/*** 消息內(nèi)容*/private String message;public Player getFrom() {return from;}public void setFrom(Player from) {this.from = from;}public long getMode() {return mode;}public void setMode(long mode) {this.mode = mode;}public Player getTo() {return to;}public void setTo(Player to) {this.to = to;}public long getTime() {return time;}public void setTime(long time) {this.time = time;}public String getMessage() {return message;}public void setMessage(String message) {this.message = message;}}
由于玩家可能在游戲過(guò)程中離開游戲,要想知道玩家的實(shí)際游戲時(shí)間,就要排除玩家離開游戲之后的時(shí)間,而不能直接使用錄像的時(shí)長(zhǎng)。如果要計(jì)算APM的話,就必須使用玩家的實(shí)際游戲時(shí)間來(lái)計(jì)算。所以在Player.java中加入playTime表示實(shí)際游戲時(shí)間,通過(guò)解析玩家離開游戲的數(shù)據(jù)塊來(lái)設(shè)置。

Player.java

/** * 游戲時(shí)間 */ private long playTime;public long getPlayTime() {return playTime; }public void setPlayTime(long playTime) {this.playTime = playTime; }
在UncompressedData類中加入對(duì)游戲進(jìn)行時(shí)的信息的解析。

UncompressedData.java

/*** 游戲進(jìn)行時(shí)的信息*/ private ReplayData replayData;public UncompressedData(byte[] uncompressedDataBytes) throws UnsupportedEncodingException, W3GException {this.uncompressedDataBytes = uncompressedDataBytes;// 跳過(guò)前4個(gè)未知字節(jié)offset += 4;// 解析第一個(gè)玩家analysisPlayerRecode();// 游戲名稱(UTF-8編碼)int begin = offset;while(uncompressedDataBytes[offset] != 0) {offset++;}gameName = new String(uncompressedDataBytes, begin, offset - begin, "UTF-8");offset++;// 跳過(guò)一個(gè)空字節(jié)offset++;// 解析一段特殊編碼的字節(jié)串,其中包含游戲設(shè)置、地圖和創(chuàng)建者analysisEncodedBytes();// 跳過(guò)PlayerCount、GameType、LanguageIDoffset += 12;// 解析玩家列表while(uncompressedDataBytes[offset] == 0x16) {analysisPlayerRecode();// 跳過(guò)4個(gè)未知的字節(jié)0x00000000offset += 4;}// GameStartRecord - RecordID、number of data bytes followingoffset += 3;// 解析每個(gè)Slotbyte slotCount = uncompressedDataBytes[offset];offset++;for(int i = 0; i < slotCount; i++) {analysisSlotRecode(i);}// RandomSeed、RandomSeed、StartSpotCountoffset += 6;// 游戲進(jìn)行時(shí)的信息解析replayData = new ReplayData(uncompressedDataBytes, offset, playerList); }public ReplayData getReplayData() {return replayData; }
修改Test.java類中main方法進(jìn)行測(cè)試。

Test.java

package com.xxg.w3gparser;import java.io.File; import java.io.IOException; import java.util.List; import java.util.zip.DataFormatException;public class Test {public static void main(String[] args) throws IOException, W3GException, DataFormatException {Replay replay = new Replay(new File("C:/Documents and Settings/Administrator/桌面/131229_[ORC]Sickofpast_VS_[NE]_AncientIsles_RN.w3g"));Header header = replay.getHeader();System.out.println("版本:1." + header.getVersionNumber() + "." + header.getBuildNumber());long duration = header.getDuration();System.out.println("時(shí)長(zhǎng):" + convertMillisecondToString(duration));UncompressedData uncompressedData = replay.getUncompressedData();System.out.println("游戲名稱:" + uncompressedData.getGameName());System.out.println("游戲創(chuàng)建者:" + uncompressedData.getCreaterName());System.out.println("游戲地圖:" + uncompressedData.getMap());List<Player> list = uncompressedData.getPlayerList();for(Player player : list) {System.out.println("---玩家" + player.getPlayerId() + "---");System.out.println("玩家名稱:" + player.getPlayerName());System.out.println("游戲時(shí)長(zhǎng):" + convertMillisecondToString(player.getPlayTime()));if(player.isHost()) {System.out.println("是否主機(jī):主機(jī)");} else {System.out.println("是否主機(jī):否");}if(player.getTeamNumber() != 12) {System.out.println("玩家隊(duì)伍:" + (player.getTeamNumber() + 1));switch(player.getRace()) {case 0x01:case 0x41:System.out.println("玩家種族:人族");break;case 0x02:case 0x42:System.out.println("玩家種族:獸族");break;case 0x04:case 0x44:System.out.println("玩家種族:暗夜精靈");break;case 0x08:case 0x48:System.out.println("玩家種族:不死族");break;case 0x20:case 0x60:System.out.println("玩家種族:隨機(jī)");break;}switch(player.getColor()) {case 0:System.out.println("玩家顏色:紅");break;case 1:System.out.println("玩家顏色:藍(lán)");break;case 2:System.out.println("玩家顏色:青");break;case 3:System.out.println("玩家顏色:紫");break;case 4:System.out.println("玩家顏色:黃");break;case 5:System.out.println("玩家顏色:橘");break;case 6:System.out.println("玩家顏色:綠");break;case 7:System.out.println("玩家顏色:粉");break;case 8:System.out.println("玩家顏色:灰");break;case 9:System.out.println("玩家顏色:淺藍(lán)");break;case 10:System.out.println("玩家顏色:深綠");break;case 11:System.out.println("玩家顏色:棕");break;}System.out.println("障礙(血量):" + player.getHandicap() + "%");if(player.isComputer()) {System.out.println("是否電腦玩家:電腦玩家");switch (player.getAiStrength()) {case 0:System.out.println("電腦難度:簡(jiǎn)單的");break;case 1:System.out.println("電腦難度:中等難度的");break;case 2:System.out.println("電腦難度:令人發(fā)狂的");break;}} else {System.out.println("是否電腦玩家:否");}} else {System.out.println("玩家隊(duì)伍:裁判或觀看者");}}List<ChatMessage> chatList = uncompressedData.getReplayData().getChatList();for(ChatMessage chatMessage : chatList) {String chatString = "[" + convertMillisecondToString(chatMessage.getTime()) + "]";chatString += chatMessage.getFrom().getPlayerName() + " 對(duì) ";switch ((int)chatMessage.getMode()) {case 0:chatString += "所有人";break;case 1:chatString += "隊(duì)伍";break;case 2:chatString += "裁判或觀看者";break;default:chatString += chatMessage.getTo().getPlayerName();}chatString += " 說(shuō):" + chatMessage.getMessage();System.out.println(chatString);}}private static String convertMillisecondToString(long millisecond) {long second = (millisecond / 1000) % 60;long minite = (millisecond / 1000) / 60;if (second < 10) {return minite + ":0" + second;} else {return minite + ":" + second;}}}
運(yùn)行程序,輸出結(jié)果:

版本:1.26.6059
時(shí)長(zhǎng):14:43
游戲名稱:當(dāng)?shù)鼐钟蚓W(wǎng)內(nèi)的游戲 (Si
游戲創(chuàng)建者:Sickofpast
游戲地圖:Maps\WAR3\(2)AncientIsles-2.w3x
---玩家1---
玩家名稱:Sickofpast
游戲時(shí)長(zhǎng):14:43
是否主機(jī):主機(jī)
玩家隊(duì)伍:1
玩家種族:獸族
玩家顏色:紅
障礙(血量):100%
是否電腦玩家:否
---玩家2---
玩家名稱:尼德霍格
游戲時(shí)長(zhǎng):14:42
是否主機(jī):否
玩家隊(duì)伍:2
玩家種族:暗夜精靈
玩家顏色:藍(lán)
障礙(血量):100%
是否電腦玩家:否
[0:10]尼德霍格 對(duì) 所有人 說(shuō):All rights reserved by Blizzard
[0:10]尼德霍格 對(duì) 所有人 說(shuō):w3g files released by www.Replays.Net.
[0:15]Sickofpast 對(duì) 所有人 說(shuō):yy上都是菜鳥啊
[0:28]尼德霍格 對(duì) 所有人 說(shuō):diyi orc!
[0:37]尼德霍格 對(duì) 所有人 說(shuō):就yy有人玩了
[0:39]Sickofpast 對(duì) 所有人 說(shuō):打ne沒(méi)贏過(guò)
[0:40]尼德霍格 對(duì) 所有人 說(shuō):中國(guó)第一ORC
[14:33]尼德霍格 對(duì) 所有人 說(shuō):For more replays, plz visit www.Replays.Net
[14:42]尼德霍格 對(duì) 所有人 說(shuō):g


參考文檔:http://w3g.deepnode.de/files/w3g_format.txt



作者:叉叉哥 ? 轉(zhuǎn)載請(qǐng)注明出處:http://blog.csdn.net/xiao__gui/article/details/18350789



總結(jié)

以上是生活随笔為你收集整理的Java解析魔兽争霸3录像W3G文件(四):解析游戏进行时的信息的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。