Jump to content
七龙的秘密基地
Search In
  • More options...
Find results that contain...
Find results in...

elstp_gf@foxmail.com

Administrators
  • Posts

    43
  • Joined

  • Last visited

elstp_gf@foxmail.com's Achievements

  1. 目录: Battleye过滤器是什么? Battleye过滤器的7种执行操作 Battleye过滤器的2种匹配类型 过滤器实战 异常函数约定 添加异常匹配 错误分析 找到规则触发者 Kick Restriction #000 关于 BEServer.cfg 中配置基本规则限制 1.BattlEye 过滤器是什么: 是什么? BE 过滤器是Arma 游戏的BattlEye 反作弊的可选功能。它们提供了一些额外的保护,可以由服务器管理员定制。 我服务器开了BE就安全了吗? 是防君子不能防小人,BE并不是全能,外挂还是能轻松注入脚本的,比如炸服脚本,但凡执行的脚本,就可以使用过滤器来阻止外挂并且记录在案,放到封神榜! 我用的infiSTAR能反外挂吗? infistart是国外付费的一款反黑客+管理工具,它的确能做一些反脚本注入,但是很有局限性,通常来说国内的人用的infi并没有完全使用它的反作弊功能,也就开了一点简单的默认 并且服务端不一,需要会技术的人来专门针对性做深度配置才能到达理想效果! 远程执行白名单CfgRemoteExec能反外挂吗? 远程执行白名单,通俗来说就是发起远程调用的函数给他加以限制,让他只能在指定的范围内使用,它确实能达到一些效果,但是对于注入脚本来说,显得很脆弱!但是绝对不能忽视CfgRemoteExec,如果不对函数加以限制,会导致非常恐怖的后果,作弊者可以让任何玩家执行代码!甚至可以让特定玩家触发BE被踢出!,除了开启函数白名单,针对一些公共的服务端,由于非常多的人知道此端的函数名和执行范围,建议对函数进行更名。 Battleye过滤器是什么?它在arma3服务端上发挥什么作用? BattlEye 过滤器是波西米亚使用的反作弊,当你的服务器启用它时,他会扫描客户端上所有正在运行的脚本,和服务端命令上特定的关键词,如果匹配到就会执行操作,至于什么操作待会在说。 如何启用Battleye过滤器? 那么,怎么样才能防止外挂? 这里介绍Battleye,国内服主基本上不会去动这个东西,因为不懂... 在你开服参数中的-profiles 指向 的文件夹内有一个BattlEye文件夹,在里面建立 scripts.txt 里面书写反作弊规则 当然,里面不单单只有scripts.txt 还有: 2.BattlEye 过滤器7种执行操作 be过滤器有7个操作,分别如下: 1 = 仅记录到 .log 文件。 2 = 仅记录到控制台窗口。 3 = 记录到 .log 文件和控制台窗口。 4 = 踢人而不生成 .log 条目。 5 = 仅踢人并记录到 .log 文件。 6 = 仅踢人并登录到控制台窗口。 7 = 踢人并登录到 .log 和控制台窗口。 控制台窗口指的就是你开arma3服务端的白色小窗口,log是你配置的过滤器文件下生成的.log文件 3. BattlEye 过滤器两种匹配类型 上下文中说的异常,指的是当过滤器抓取到了sqf,假设你过滤器写了一个函数和规则, 恰好客户端触发了,这就产生了异常sqf,而异常通常代指你写的规则 != 完全匹配符 当规则有这条,就表示参数必须是完全匹配,意思是如果你执行了某函数,在scripts里定义的关键词函数整个语句都必须与异常完全匹配 ! 包含匹配符 当规则有这一条,就表示参数里包含了异常 4.过滤器规则实践 添加一个关键词,当发现这个关键词时T人打开scripts.txt 在开头第一行写//new2 这个是协议规定,不能改的 然后另起一行 异常是支持正则表达式的,然后 凡是异常中的代码带引号都要反斜杠,带空格的代码都要带引号 异常规则 如何写一个规则?大概格式是这样 要执行的操作ID(1-7) 关键词 规则 在scripts中是这样: 执行方式 触发关键词 匹配类型 异常 1 "allPlayers" 意思是,当有人执行了allPlayers函数,会被记录,为什么用引号,其实用不用无所谓吧,有空格就要引号 1.使用scripts.txt通过记录作弊者执行的脚本来做反作弊过滤器 1 "" 里面是空的,表示客户端执行的所有脚本都会被记录到.log文件 为什么要怎么做?因为这样将有利于分析外挂使用者注入的脚本,他执行的脚本将会被记录! 2.使用createvehicle.txt过滤器 禁止某些东西在服务器内生成 假如我服务器是没有坦克的,想当玩家生成了一辆坦克,就把这个人踢掉,并且记录他执行的脚本代码该如何做呢? 这里拿T100来说,T100的class是 O_Truck_03_medical_F 创建载具的函数是 createVehicle createvehicle.txt 是当玩家执行创建对象的时候执行,这个对象可以是载具,或者其他 ,假如我在 createvehicle.txt 过滤器里写了这一句规则 5 " O_Truck_" !" O_Truck_03" 解释: 表示从玩家客户端捕获脚本以 O_Truck_ 开头的. ! 感叹号上面说过,规则里要包含这一条,规则是 O_Truck_03 然后某玩家执行了创建载具函数: createVehicle ["O_Truck_03_ABC_F",getPosATL player,[],10,"NONE"]; 是不会被T和记录日志,为什么? 因为 ! 的匹配机制是包含,上面玩家执行的创建sqf确实包含了 O_Truck_03_medical,所以他不会被T 什么情况下会被踢出去? createVehicle ["O_Truck_04_ABC_F",getPosATL player,[],10,"NONE"]; 当玩家执行这句的时候会被T,因为捕获到了O_Truck,但是O_Truck_03 这个规则 内不包含O_Truck_04_ABC_F 如果把匹配类型改成 != 5 "O_Truck" !="O_Truck_03_medical_F" 上面的两个实例都会被T,因为参数不完全匹配为O_Truck_03_medical_F,什么情况下不会被T? createVehicle ["O_Truck_03_medical_F",getPosATL player,[],10,"NONE"]; 是不会被T的,因为他完全匹配,一般来说,在createvehicle.txt使用 ! 异常比 != 更有效,但是在scripts中尽量使用!= 上面说的这些规则,在scripts.txt里的工作方式是不一样的! 5.异常函数约定 这个其实不是官方的内容,官方也没有,这是我的想法,我称之为约定,什么叫异常sqf代码函数约定,假设我服务端的代码中遍历了全体玩家 这是外挂注入脚本用的最多的方式,我们来约定一种函数,只能通过这种函数来调用,否则识别为黑客代码T掉 我们在任务里封装一个函数假设为: destiny_fnc_allPlayers ,其内容为 _DestinyAllPlayers = allPlayers _DestinyAllPlayers 调用该函数返回一个全部玩家数组,任务混淆,防止外挂使用者扒取代码,然后在过滤器中写 5 "allPlayers" !="_DestinyAllPlayers = allPlayers" 然后代码中全部使用到allPlayers的函数全部替换为 destiny_fnc_allPlayers 当外挂注入者执行了一个杀死全体玩家的脚本,他肯定会这样写 { _x setDamage 1; } forEach allPlayers; 意思是,循环全体玩家,然后全部杀死,但是在执行前会被反作弊扫描到,从而匹配到我们上面写的规则,触发异常, 异常是forEach allPlayers; 然后被服务器T掉 因为他使用了allPlayers,但是它使用的规则不是 _DestinyAllPlayers = allPlayers,只要不是全部T掉,这就是我说的约定,客户端脚本与我写的规则做了一个约定,约定只能使用这种格式来获取全体玩家! 凡是违反约定的执行代码方式,都会被T,从而达到反作弊效果! 6.添加异常为完全匹配 使用上面的方式因为是!=,完全匹配会遇到一个问题,就是可能官方也用到了这个,或者你用了别人的任务服务端也会出现这个,不好加以约束,我该如何呢? 我们可以这样,1 "allPlayers" 来记录用到allPlayers的函数 然后我们会在scripts.log中看到触发的代码,例如: 22.04.2022 23:11:16: RIO (192.168.0.111:2304) b8a252f8aabd48365cf071e5a2bf011d - #31 "xxx_fncxxx = { params ["_object"]; _p = allPlayers;" 复制最外面的引号之间的代码,如下: "xxx_fncxxx = { params ["_object"]; _p = allPlayers;" 遵循上述规则,在每次出现的引号前"加上反斜杠\。由于这是针对scripts.txt,我们可以忽略正则表达式元字符。Notepad++ 等文本编辑器可用于快速替换所有出现的"with \"。 \"xxx_fncxxx = { \n params ["_object"]; \n _p = allPlayers;\" 格式化为一行 \"xxx_fncxxx = {\nparams ["_object"];\n_p = allPlayers;\" 由于包含空格,就要引号阔起来,这个异常就好了,然后需要添加一个! 或者 != 让他识别为异常,我们使用!= 因为更安全 !="\"xxx_fncxxx = {\nparams ["_object"];\n_p = allPlayers;\"" 最后,必须将整行代码添加到与相对应的地方,假如T人 5 allPlayers !="\"xxx_fncxxx = {\nparams ["_object"];\n_p = allPlayers;\"" 7.错误分析 找到规则触发者 Kick Restriction #000 有时候进去服务器被T,例如显示 23:05:25 | Player #31 RIO (192.168.0.111:2304) (b8a252f8aabd48365cf071e5a2bf011d) has been kicked by BattlEye: Script Restriction #110 显示一些正常的函数都触发了,怎么找到并且排除这个问题呢? 这里提供了3个消息, 发生时间,玩家名称, 被T的消息和编号 首先找到触发日志.这里提示我触发了scripts.txt,就去找到scripts.log 根据时间找到 编号为 #110的,就可以看见我们触发了什么! 22.04.2022 23:05:25: RIO (192.168.0.111:2304) b8a252f8aabd48365cf071e5a2bf011d - #110 "namespace getvariable ["BIS_fnc_arsenal_light",objnull]; deletevehicle _center; deletevehicle _sphere; deletevehicle _light; 上面就是触发的代码, 如果想完全包含,按照低6章教程即可! 8.配置BEServer.cfg的基本规则 很多人仅仅知道在这个文件里配置rcon,其实它还可以提供强大的规则,有些服主rcon无法连接,此文件的规则不生效,请开启你的BE反作弊! 在你开服参数中的-profiles 指向 的文件夹内有一个BattlEye文件夹,在服务器运行时,它会自动重命名为 BEServer_active_xxxx.cfg 以确保安全。它必须至少包含服务器 RconPort 和 RConPassword。如果你没有此文件可以新建一个txt文件将文件格式改为cfg,使用x64开服就命名为:BEServer_x64.cfg,如果不是x64将它命名为BEServer.cfg 首先我们来看一个基本示例: RConPassword rcon123456789 RconPort 7410 MaxAddBackpackCargoPerInterval 20 1 MaxAddMagazineCargoPerInterval 400 1 MaxAddWeaponCargoPerInterval 75 1 MaxCreateVehiclePerInterval 150 1 MaxDeleteVehiclePerInterval 100 1 MaxSetDamagePerInterval 3 1 MaxSetPosPerInterval 10 1 格式为: Max[Command]PerInterval [最大使用次数] [以秒为单位的时间] 在示例中,如果 setDamage 在 1 秒或更短的时间内使用了 30 次或更多次,它将触发 setDamage 计数限制,并且会被T出服务器。 用于每秒最大使用次数的值会因 mod 的不同而有很大差异。上述值是一个松散示例。为了获得最佳值,您需要从可能的最严格(最低)限制开始试验每个命令,并增加它直到没有引起踢出服务器。这将需要大量测试,因为只有某些动作或事件可能会触发踢出玩家。也可以提高时间,让限制更加严格。 命令: 最后 BE过滤器指南看到这里我相信你已经拥有了会自己配置反作弊规则的能力! 来首BGM庆祝一下! 参考: https://github.com/AsYetUntitled/Framework/wiki/BattlEye-Filters https://opendayz.net/threads/a-guide-to-battleye-filters.21066/
  2. Version 1.0.0

    485 downloads

    使用Fiddler抓取抖音弹幕插件 我做成了插件,不适合小白!如果你动手能力较强可以下载插件使用!
  3. Version 1.0.1

    30 downloads

    注意编辑config.yml里面的配置信息! 这个机器人能做什么? 输入#帮助: 输入服主设置的自定义关键词可查询服务器 功能列表 输入 #全体玩家 服务器关键词 |来获取全部玩家 输入 #换图 服务器关键词 地图名称 |来更换服务器地图 输入 #换图列表 |来查看服务器地图列表 输入 #踢出 服务器关键词 玩家名称 理由 |来踢出一名玩家 输入 #封锁 服务器关键词 steam64ID 小时(0表示永久) 理由 |来封锁一个玩家 输入 #解ban 服务器关键词 steam64ID |来解封一名玩家 输入 #VIP列表 服务器关键词 |来查看VIP列表 输入 #添加VIP 服务器关键词 玩家名称 steam64ID |来添加一个VIP 输入 #删除VIP 服务器关键词 玩家名称 steam64ID |来删除一个VIP 输入 #处罚 服务器关键词 玩家名称 理由 |来处罚一名玩家 输入 #重置投票触发阈值 服务器关键词 |来重置 输入 #广播消息 消息内容 |向服务器发送一条消息 输入 #管理员列表 服务器关键词 |查询服务器的全部管理员 输入 #添加管理员 服务器关键词 玩家名称 steam64ID 角色(owner是服主) |来添加一个管理员 输入 #删除管理员 服务器关键词 玩家名称 steam64ID 角色(owner是服主) |来删除一个管理员
  4. 打开UE4,点击左上角文件>然后点击新建C++类 添加一个蓝图函数库 起一个名字,类型改成公共,让其他可访问 等待打开VS2019 在右侧我们可以看见刚刚建立的文件,点展开Source里面就可以看见你自己建立的函数库,有2个,一个是头文件,即为.h主要工作是暴露接口给UE蓝图用, 另一个为.cpp 是我们主要实现业务逻辑的地方 比如我的是MyBlueprintFunctionLibrary.h和MyBlueprintFunctionLibrary.cpp 首先打开MyBlueprintFunctionLibrary.h 声明一个方法 UFUNCTION(BlueprintCallable表示蓝图可使用, Category = "就是你右击面板的时候显示在哪一个分组", meta = (DeterminesOutputType = "是什么类型的" )) 然后给了一个静态修饰,值得注意的是,UE里面每一个类型开头都是大写的,假如Actor 类型前面就要加A,例如AActor, 假如你的函数蓝图是 MyBlueprintFunctionLibrary 前面就要加 U 即为UMyBlueprintFunctionLibrary 这些是千万要注意的! 好了,这里给这个函数起名为CloneActor,输入类型是Actor 返回类型也是Actor,这时候会看见CloneActor函数报错,那是因为我们没有实现它,现在打开MyBlueprintFunctionLibrar.cpp 实现功能 UFUNCTION(BlueprintCallable, Category = "ActorFuncions", meta = (DeterminesOutputType = "InputActor")) static AActor* CloneActor(AActor* InputActor); /// <summary> /// 克隆actor /// </summary> /// <param name="InputActor"></param> /// <returns></returns> AActor* UMyBlueprintFunctionLibrar::CloneActor(AActor* InputActor) // 注意: 在库名称前加一个U,前面说了的 { UWorld* World = InputActor->GetWorld();//获取世界主要用来调用 SpawnActor 函数,因为SpawnActor是动态的,在世界中生成的动态actor FActorSpawnParameters params;//传递给 SpawnActor 函数的可选参数的结构 params.Template = InputActor;//把原来的actor参数拷贝起来 UClass* ItemClass = InputActor->GetClass();//获取原来的actor的类型 AActor* const SpawnedActor = World->SpawnActor<AActor>(ItemClass, params);//生成一个新的Actor 并且把参数给加上 return SpawnedActor;//返回新的actor } 点击编译 等待编译完成 如果你编译出错,请检查,没有问题出现莫名其妙的问题,点文件刷新Visual Studio项目试试或者请重启项目,再不行直接重建,我也不知道为什么有时候没问题就是死活无法编译 保存后,在蓝图里右击调用 至此,使用C++制作一个蓝图函数库教程完成!
  5. 使用rcon可以用 "github.com/verocity-gaming/rcon" 的rcon依赖,新建Go项目,然后导入 import ( "github.com/verocity-gaming/rcon" ) 初始化rcon连接 IP例如127.0.0.1:27015 c, err := rcon.New(ip, password) if err != nil { println("连接服务器失败!") panic(err) } 假如我要执行RCON指令来换图 mapName 这个变量就是地图名了 p := c.SetMap(rcon.MapName(mapName)) if p == nil { fmt.Sprintf("操作成功!") } else { fmt.Sprintf("%s", p) } 然后下面是可用的功能 可以自行实现: func New(addr string, password string) (*Conn, error) func (c *Conn) AdminAdd(a Admin) error func (c *Conn) AdminGroups() ([]string, error) func (c *Conn) AdminRemove(a Admin) error func (c *Conn) Admins() ([]Admin, error) func (c *Conn) AutoBalance() (bool, error) func (c *Conn) AutoBalanceThreshold() (int, error) func (c *Conn) BanPermanently(p Player, reason, admin string) error func (c *Conn) BanRemove(p Player) error func (c *Conn) BanTemporarily(p Player, hours int, reason, admin string) error func (c *Conn) Close() error func (c *Conn) IdleTime() (time.Duration, error) func (c *Conn) Kick(p Player, reason string) error func (c *Conn) Map() (Map, error) func (c *Conn) Maps() ([]Map, error) func (c *Conn) MaxPing() (time.Duration, error) func (c *Conn) Name() (string, error) func (c *Conn) PermanentlyBanned() ([]Ban, error) func (c *Conn) Player(username string) (Player, error) func (c *Conn) Players() ([]Player, error) func (c *Conn) Profanities() ([]string, error) func (c *Conn) Punish(p Player, reason string) error func (c *Conn) ResetVoteKickThreshold() error func (c *Conn) Rotation() ([]Map, error) func (c *Conn) RotationAdd(n MapName) error func (c *Conn) RotationRemove(n MapName) error func (c *Conn) SetAutoBalance(enabled bool) error func (c *Conn) SetAutoBalanceThreshold(diff int) error func (c *Conn) SetBroadcast(message string) error func (c *Conn) SetIdleTime(m time.Duration) error func (c *Conn) SetMap(n MapName) error func (c *Conn) SetMaxPing(ms time.Duration) error func (c *Conn) SetProfanities(words ...string) error func (c *Conn) SetQueueLength(length int) error func (c *Conn) SetSwitchTeamCooldown(m time.Duration) error func (c *Conn) SetSwitchTeamNow(p Player) error func (c *Conn) SetSwitchTeamOnDeath(p Player) error func (c *Conn) SetVIPSlots(slots int) error func (c *Conn) SetVoteKick(enabled bool) error func (c *Conn) SetVoteKickThreshold(pairs ...VoteKickThreshold) error func (c *Conn) Slots() (numerator, denominator int, err error) func (c *Conn) SwitchTeamCooldown() (time.Duration, error) func (c *Conn) TemporarilyBanned() ([]Ban, error) func (c *Conn) UnsetProfanities(words ...string) error func (c *Conn) VIPAdd(v VIP) error func (c *Conn) VIPRemove(v VIP) error func (c *Conn) VIPSlots() (int, error) func (c *Conn) VIPs() ([]VIP, error) func (c *Conn) VoteKick() (bool, error) 写的一个小栗子: TEST.go
  6. 查询steam游戏服务器信息API,可查询arma3,dayz等steam游戏 新建类 ServerPlayer.java 封装玩家信息 package com.destiny.kaiheila.destinybot.SteamServerQuery; public class ServerPlayer { public int PlayerIndex; public String PlayerName; public long PlayerScore; public float PlayerDuration; public ServerPlayer(int Index, String Name, long Score, float Duration) { this.PlayerIndex = Index; this.PlayerName = Name; this.PlayerScore = Score; this.PlayerDuration = Duration; } public int getIndex() { return this.PlayerIndex; } public String getName() { return this.PlayerName; } public long getScore() { return this.PlayerScore; } public float getDuration() { return this.PlayerDuration; } } 新建 SteamServerChallenge.java steam查询头 package com.destiny.kaiheila.destinybot.SteamServerQuery; public class SteamServerChallenge { public static int HEADER = (byte)0x41; } 新建 SteamServerEnvironment.java 专用服务器操作系统标识 package com.destiny.kaiheila.destinybot.SteamServerQuery; public class SteamServerEnvironment { public static int LINUX = 108; public static int WINDOWS = 119; public static int MAC = 109; } 新建 SteamServerInfo.java 服务器信息 package com.destiny.kaiheila.destinybot.SteamServerQuery; import java.nio.ByteBuffer; import java.nio.ByteOrder; public class SteamServerInfo { private int position = 0; public static byte HEADER = (byte)0x49; private int ServerProtocol; private byte[] ServerName; private byte[] ServerMap; private byte[] ServerFolder; private byte[] ServerGame; private short ServerAppID; private int ServerPlayers; private int ServerMaxPlayers; private int ServerBots; private int ServerType; private int ServerEnvironment; private int ServerVisibility; private int ServerVAC; private byte[] ServerVersion; private int ServerEDF; public SteamServerInfo(byte[] Buffer) { this.ServerProtocol = Buffer[0]; this.position++; int ServerNameLength = this.getStringLenght(this.position, Buffer); this.ServerName = new byte[ServerNameLength]; System.arraycopy(Buffer, this.position, this.ServerName, 0, ServerNameLength); this.position = this.position + ServerNameLength + 1; int ServerMapLength = this.getStringLenght(this.position, Buffer); this.ServerMap = new byte[ServerMapLength]; System.arraycopy(Buffer, this.position, this.ServerMap, 0, ServerMapLength); this.position = this.position + ServerMapLength + 1; int ServerFolderLength = this.getStringLenght(this.position, Buffer); this.ServerFolder = new byte[ServerFolderLength]; System.arraycopy(Buffer, this.position, this.ServerFolder, 0, ServerFolderLength); this.position = this.position + ServerFolderLength + 1; int ServerGameLength = this.getStringLenght(this.position, Buffer); this.ServerGame = new byte[ServerGameLength]; System.arraycopy(Buffer, this.position, this.ServerGame, 0, ServerGameLength); this.position = this.position + ServerGameLength + 1; this.ServerAppID = ByteBuffer.wrap(Buffer, this.position, this.position + 1).order(ByteOrder.LITTLE_ENDIAN).getShort(); this.position = this.position + 2; this.ServerPlayers = Buffer[this.position]; this.position++; this.ServerMaxPlayers = Buffer[this.position]; this.position++; this.ServerBots = Buffer[this.position]; this.position++; this.ServerType = Buffer[this.position]; this.position++; this.ServerEnvironment = Buffer[this.position]; this.position++; this.ServerVisibility = Buffer[this.position]; this.position++; this.ServerVAC = Buffer[this.position]; this.position++; int ServerVersionLength = getStringLenght(this.position, Buffer); this.ServerVersion = new byte[ServerVersionLength]; System.arraycopy(Buffer, this.position, this.ServerVersion, 0, ServerVersionLength); this.position = this.position + ServerVersionLength + 1; this.ServerEDF = Buffer[this.position]; } public int getProtocol() { return this.ServerProtocol; } public String getName() { return new String(this.ServerName); } public String getMap() { return new String(this.ServerMap); } public String getFolder() { return new String(this.ServerFolder); } public String getGame() { return new String(this.ServerGame); } public short getAppID() { return this.ServerAppID; } public int getPlayers() { return this.ServerPlayers; } public int getMaxPlayers() { return this.ServerMaxPlayers; } public int getBots() { return this.ServerBots; } public int getType() { return this.ServerType; } public int getEnvironment() { return this.ServerEnvironment; } public int getVisibility() { return this.ServerVisibility; } public int getVAC() { return this.ServerVAC; } public String getVersion() { return new String(this.ServerVersion); } public int getEDF() { return this.ServerEDF; } private int getStringLenght(int start, byte[] buffer) { for (int i = start; i < buffer.length; i++) { if (buffer[i] == 0) { return i - start; } } return 0; } @Override public String toString() { return "Protocol : " + this.getProtocol() + "\nName : " + this.getName() + "\nMap : " + this.getMap() + "\nFolder : " + this.getFolder() + "\nGame : " + this.getGame() + "\nAppID : " + this.getAppID() + "\nPlayers : " + this.getPlayers() + "\nMax Players : " + this.getMaxPlayers() + "\nBots : " + this.getBots() + "\nServer Type : " + (char)this.getType() + " (d = DEDICATED|l = NON-DEDICATED|p = SourceTV/proxy)\nEnvironment : " + (char)this.getEnvironment() + " (l = Linux|w = Windows|m = MAC)\nVisibility : " + this.getVisibility() + " (0 = Public|1 = Private)\nVAC : " + this.getVAC() + " (0 = UNSECURED|1 = SECURED)\nVersion : " + this.getVersion() + "\nExtra Data Flag (EDF) : " + this.getEDF(); } } 新建 SteamServerPlayer.java 服务器玩家信息 package com.destiny.kaiheila.destinybot.SteamServerQuery; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class SteamServerPlayer { private int position = 0; public static byte HEADER = (byte)0x44; public int PlayersLength; public ServerPlayer[] Players; public SteamServerPlayer(byte[] Buffer) { this.PlayersLength = Buffer[this.position]; this.Players = new ServerPlayer[this.getPlayersLength()]; this.position++; for (int i = 0; i < this.getPlayersLength(); i++) { int PlayerIndex = Buffer[this.position]; this.position++; int PlayerNameLength = getStringLenght(this.position, Buffer); byte[] PlayerName = new byte[PlayerNameLength]; System.arraycopy(Buffer, this.position, PlayerName, 0, PlayerNameLength); this.position = this.position + PlayerNameLength + 1; long PlayerScore = ((Buffer[this.position + 3] & 0xFFL) << 24) | ((Buffer[this.position + 2] & 0xFFL) << 16) | ((Buffer[this.position + 1] & 0xFFL) << 8) | ((Buffer[this.position + 0] & 0xFFL) << 0); this.position = this.position + 4; float PlayerDuration = ByteBuffer.wrap(new byte[] {Buffer[this.position], Buffer[this.position+1], Buffer[this.position+2], Buffer[this.position+3]}).order(ByteOrder.LITTLE_ENDIAN).getFloat(); this.position = this.position + 4; this.Players[i] = new ServerPlayer(PlayerIndex, new String(PlayerName), PlayerScore, PlayerDuration); } } public int getPlayersLength() { return this.PlayersLength; } public ServerPlayer[] getPlayers() { return this.Players; } private int getStringLenght(int start, byte[] buffer) { for (int i = start; i < buffer.length; i++) { if (buffer[i] == 0) return i - start; } return 0; } public List<Map<String,Object>> playerList (){ List<Map<String,Object>> listMap = new ArrayList<>(); for (ServerPlayer Player : this.getPlayers()) { Map<String,Object> map = new HashMap<>(); map.put("playerName",Player.getName() ); map.put("Score", Long.parseLong(String.valueOf(Player.getScore()))); map.put("Duration",Math.round(Player.getDuration() / 3600) + ":" + Math.round((Player.getDuration() % 3600) / 60) + ":" + Math.round((Player.getDuration() % 3600) % 60)); listMap.add(map); } return listMap; } public String toString() { String PlayerTable = ""; for (ServerPlayer Player : this.getPlayers()) { PlayerTable += Player.getName() + ((Player.getName().length() <= 7) ? "\t\t\t\t" : ((Player.getName().length() <= 15) ? "\t\t\t" : ((Player.getName().length() <= 23) ? "\t\t" : "\t"))); PlayerTable += new Long(Player.getScore()).intValue() + "\t\t"; PlayerTable += Math.round(Player.getDuration() / 3600) + ":" + Math.round((Player.getDuration() % 3600) / 60) + ":" + Math.round((Player.getDuration() % 3600) % 60) + "\n"; } return "Players : " + this.getPlayersLength() + "\nPlayer Name :\t\t\tScore :\t\tDuration:\n" + PlayerTable; } } 新建SteamServerQuery.java steam主服务器查询 package com.destiny.kaiheila.destinybot.SteamServerQuery; import java.net.*; public class SteamServerQuery { private InetAddress ServerAddress; private int ServerPort; private DatagramSocket UDPClient; public SteamServerQuery(InetAddress Address, int Port) { try { this.UDPClient = new DatagramSocket(); this.ServerAddress = Address; this.ServerPort = Port; } catch (SocketException e) { e.printStackTrace(); } } public SteamServerQuery(String Address, int Port) throws UnknownHostException { this(InetAddress.getByName(Address), Port); } public SteamServerQuery(String Address) throws UnknownHostException { this(Address.split(":")[0], Integer.parseInt(Address.split(":")[1])); } public DatagramSocket getDatagramSocket() { return UDPClient; } public SteamServerInfo getInfo() throws Exception { byte[] InfoHeader = new byte[25]; InfoHeader[0] = (byte)0xFF; InfoHeader[1] = (byte)0xFF; InfoHeader[2] = (byte)0xFF; InfoHeader[3] = (byte)0xFF; InfoHeader[4] = (byte)0x54; byte[] SourceString = new String("Source Engine Query").getBytes(); System.arraycopy(SourceString, 0, InfoHeader, 5, SourceString.length); InfoHeader[5 + SourceString.length] = (byte) 0x00; DatagramPacket SendInfoPacket = new DatagramPacket(InfoHeader, InfoHeader.length, this.ServerAddress, this.ServerPort); this.UDPClient.setSoTimeout(3000); this.UDPClient.send(SendInfoPacket); byte[] ReceivedBuffer = new byte[512]; DatagramPacket ReceivedInfoPacket = new DatagramPacket(ReceivedBuffer, ReceivedBuffer.length); UDPClient.setSoTimeout(3000); this.UDPClient.receive(ReceivedInfoPacket); if (ReceivedBuffer[0] == (byte)0xFF && ReceivedBuffer[1] == (byte)0xFF && ReceivedBuffer[2] == (byte)0xFF && ReceivedBuffer[3] == (byte)0xFF && ReceivedBuffer[4] == SteamServerInfo.HEADER) { byte[] ServerInfoBuffer = new byte[ReceivedBuffer.length - 5]; System.arraycopy(ReceivedBuffer, 5, ServerInfoBuffer, 0, ServerInfoBuffer.length); return new SteamServerInfo(ServerInfoBuffer); } return null; } public SteamServerPlayer getPlayer() throws Exception{ byte[] PlayerHeader = this.getChallenge(); PlayerHeader[4] = (byte)0x55; DatagramPacket SendPlayerPacket = new DatagramPacket(PlayerHeader, PlayerHeader.length, this.ServerAddress, this.ServerPort); this.UDPClient.send(SendPlayerPacket); byte[] ReceivedPlayerBuffer = new byte[1024]; DatagramPacket ReceivedPlayerPacket = new DatagramPacket(ReceivedPlayerBuffer, ReceivedPlayerBuffer.length); this.UDPClient.setSoTimeout(3000); this.UDPClient.receive(ReceivedPlayerPacket); if (ReceivedPlayerBuffer[0] == (byte)0xFF && ReceivedPlayerBuffer[1] == (byte)0xFF && ReceivedPlayerBuffer[2] == (byte)0xFF && ReceivedPlayerBuffer[3] == (byte)0xFF && ReceivedPlayerBuffer[4] == SteamServerPlayer.HEADER) { byte[] ServerPlayerBuffer = new byte[ReceivedPlayerBuffer.length - 5]; System.arraycopy(ReceivedPlayerBuffer, 5, ServerPlayerBuffer, 0, ServerPlayerBuffer.length); return new SteamServerPlayer(ServerPlayerBuffer); } else { System.err.println("ERROR Player Packet !"); return null; } } public byte[] getChallenge() throws Exception { byte[] ChallengeHeader = new byte[9]; ChallengeHeader[0] = (byte)0xFF; ChallengeHeader[1] = (byte)0xFF; ChallengeHeader[2] = (byte)0xFF; ChallengeHeader[3] = (byte)0xFF; ChallengeHeader[4] = (byte)0x55; ChallengeHeader[5] = (byte)0xFF; ChallengeHeader[6] = (byte)0xFF; ChallengeHeader[7] = (byte)0xFF; ChallengeHeader[8] = (byte)0xFF; DatagramPacket SendChallengePacket = new DatagramPacket(ChallengeHeader, ChallengeHeader.length, this.ServerAddress, this.ServerPort); this.UDPClient.send(SendChallengePacket); byte[] ReceivedChallengeBuffer = new byte[9]; DatagramPacket ReceivedChallengePacket = new DatagramPacket(ReceivedChallengeBuffer, ReceivedChallengeBuffer.length); this.UDPClient.setSoTimeout(3000); this.UDPClient.receive(ReceivedChallengePacket); if(ReceivedChallengeBuffer[0] == (byte)0xFF && ReceivedChallengeBuffer[1] == (byte)0xFF && ReceivedChallengeBuffer[2] == (byte)0xFF && ReceivedChallengeBuffer[3] == (byte)0xFF && ReceivedChallengeBuffer[4] == SteamServerChallenge.HEADER) { return ReceivedChallengeBuffer; } else { System.err.println("ERROR Challenge Packet !"); return new byte[9]; } } } 使用方法: //参数 IP:查询端口 SteamServerQuery ServerQuery = new SteamServerQuery("0.0.0.0:2303"); //从服务器获取服务器信息 SteamServerInfo ServerInfo = ServerQuery.getInfo(); //从服务器获取玩家信息 SteamServerPlayer player = ServerQuery.getPlayer(); ServerInfo..... player.... SteamServerQuery.7z
×
×
  • Create New...