TShock中文插件库TShock中文插件库
插件文档
插件开发
其他文档
  • 简体中文
  • en-US
GitHub
插件文档
插件开发
其他文档
  • 简体中文
  • en-US
GitHub
  • Caiの插件教程
  • Part 0.配置开发环境​
  • Part 1.编写你的第一个插件​
  • Part 2.初步了解TShock插件模板​
  • Part 3.添加新命令​
  • Part 4.挂钩钩~
  • Part 5.玩家对象​
  • Part 6.0 数据包基础
  • Part 6.1 读取数据包 (超难理解)
  • Part 6.1发送数据包​
  • Part 6.5.1 数据包参考表格(1.4.4.9) (by @xuyuwtu & @ACaiCat)
  • Part 6.5.2 客户端与服务端的连接 (不要求学会,仅做参考)
  • Part 7.插件配置文件 (临时加更)

Part 5.玩家对象​

本章你将学到:

  • 学习TSPlayer类的字段、方法等
  • 学会Player类的常用字段、方法等

什么是TSPlayer类?

  • TSPlayer是TShock的一个类,属于"TShock"的东西。这个类往往包含TShock服务器最常用的玩家数据和方法,其中的方法字段几乎全部对服务端都是有效的。

什么是Player类?

  • Player是Terraria的一个类,属于"Terraria"的东西。这个类包含Terraria常用的玩家数据,这个类里的字段、方法繁多且杂乱,而且还包含了许多客户端才有效的字段、方法,只有部分方法、字段服务端可用。

TSPlayer对象

获取TSPlayer对象

获取玩家对象的方法有多种,主要根据你的需要选择,下面的代码通过不同的方式获取TSPlayer玩家对象

  • TSPlayer.FindByNameOrID()方法 (主要用于命令)

TSPlayer.FindByNameOrID()会返回一个列表,包含所有匹配的玩家对象,当然没有匹配结果会返回一个空列表

private void Test(CommandArgs args)
{
    TSPlayer plr;
    List<TSPlayer> plrs = TSPlayer.FindByNameOrID(args.Parameters[0]);
    //args.Parameters[0]为玩家名字或索引
    //如果参数为 tsi:2 则表示索引为2的玩家
    //如果参数为 tsn:233 则表示名字为233的玩家
    if (plrs.Count == 0)
    {
        args.Player.SendErrorMessage("玩家不存在!"); //发送错误消息
        return;
    }
    else if (plrs.Count > 1)
    {
        args.Player.SendMultipleMatchError(plrs.Select(p => p.Name)); //发送多个匹配结果错误
        //会向玩家发送:
        //找到多个匹配项-无法判断哪个是正确的:
        //玩家1,玩家2....
        //用"半角双引号"包裹关键字来搜索名字带有空格的物品
        //使用 tsi:序号 或者 tsn:名称 来区分含有数字的名称.
        return;
    }
    else
    {
        plr = plrs[0];
        //找到匹配的玩家对象
    }
}
  • Index索引获取
TSPlayer plr= TShock.Players[index]; //index为玩家索引

这种方法非常重要,常常用在ServerApi钩子获取TSPlayer对象

//插件加载时执行的代码
public override void Initialize()
{
    ServerApi.Hooks.ServerChat.Register(this, OnChat);
    Commands.ChatCommands.Add(new Command(Test, "test"));
}

private void OnChat(ServerChatEventArgs args)
{
    TSPlayer plr = TShock.Players[args.Who]; //获取钩子触发者的TSPlayer对象
}

//插件卸载时执行的代码
protected override void Dispose(bool disposing)
{
    if (disposing)
    {
        ServerApi.Hooks.ServerChat.Deregister(this, OnChat);
    }
    base.Dispose(disposing);
}
  • 通过Where进行搜索

这种方法用于寻找特定条件的玩家,是一种万能的方法,示例代码如下:
(p!=null 很重要,请务必写上)

string name = "233"; //假设的量
string acc = "233";
string ip = "127.0.0.1";

TSPlayer plr;
//var是推断类型的意思,使用var编译器会自己判断变量的类型,下面是一种偷懒的写法,完整的应该是IEnumerable<TSPlayer> plrs
var plrs= TShock.Players.Where(p => p!=null && p.Name == name); //通过玩家名字获取玩家对象
//var plrs = TShock.Players.Where(p => p != null && p.Account.Name == acc); //通过账号名字获取玩家对象
//var plrs = TShock.Players.Where(p => p != null && p.IP == ip); //通过IP获取玩家对象
//var plrs = TShock.Players.Where(p => p != null && p.Account.Name == acc && p.IP == ip ); //通过IP和账号名字获取玩家对象
//当然如果你需要的是单个玩家对象,你需要使用下列的代码进行判断
//如果你需要的符合条件的所有玩家,那么plrs就是结果
if (plrs.Count() == 0)
{
    //玩家对象不存在
    return;
}
else if (plrs.Count() > 1)
{
    //玩家对象存在多个
    return;
}
else
{
    plr = plrs.First();
    //plr即为玩家对象
}

TSPlayer对象中常用字段和方法

  • TSPlayer类中的常用属性(加粗的要求记忆,其他的参考就好)
名称类型作用/含义
Namestring玩家角色名
Indexint玩家索引
GroupGroup玩家所处的组对象
AccountUserAccount玩家的账号对象
TPlayerPlayer玩家的Player对象
UUIDstring玩家客户端的UUID
IPstring玩家IP地址
Xfloat玩家的X坐标
Yfloat玩家的Y坐标
TileXint玩家所处图格的X坐标(TileX=[取整]X/16)
TileYint玩家所处图格的Y坐标(TileY=[取整]X/16)
Teamint玩家队伍(无队伍0,红队1,绿队2,蓝队3,黄队4,粉队5)
RealPlayerbool正常玩家(true);控制台或REST(false) [主要用于在命令中区分玩家和控制台]
RespawnTimerint玩家重生计时器(单位:秒)
LoginAttemptsint玩家登录尝试次数
Activebool玩家的活动状态(通常为true)
ConnectionAlivebool玩家的连接状态(正常为true)
DataWhenJoinedPlayerData玩家加入时的人物数据(即本地存档)
InventoryIEnumerable<Item>玩家背包(前5行)
AccessoriesIEnumerable<Item>玩家的配饰
InventorySlotAvailablebool玩家背包是否有多余
SelectedItemItem玩家当前手持的物品
Stateint玩家的客户端状态(与加入服务器有关)
LastThreatDateTime玩家上次被冻结(Disable)的时间(Utc)
PaintThresholdint玩家在上一秒漆墙的数量
ProjectileThresholdint玩家在上一秒生成弹幕的数量
TileKillThresholdint玩家在上一秒破坏图格的数量
TileLiquidThresholdint玩家在上一秒放置液体的数量
TilePlaceThresholdint玩家在上一秒放置图格的数量
HealOtherThresholdint玩家在上一秒治疗玩家的次数
TilesCreatedDictionary<Vector2, ITile>玩家放置的待摧毁图格(与图格回弹有关)
TilesDestroyedDictionary<Vector2, ITile>玩家摧毁的待放置图格(与图格回弹有关)
AwaitingNamebool等待玩家获取区域名字
AwaitingNameParameterstring[]获取区域名字时,区域的控制字符(-u 包含保护的区域,-z 包含区域Z索引,-p 玩家将持续接受区域编辑信息?)
AwaitingTempPointint等待玩家设置区域临时点(0未等待,1点1等待中,2点2等待中)
  • TSPlayer类中的字段(加粗的要求记忆,其他的参考就好)
字段名类型作用/含义
AcceptingWhispersbool玩家是否接受耳语(私聊) (/w)
ActiveChestint玩家当前打开的箱子ID (玩家没有开箱子则未-1)
AwaitingResponseDictionary<string, Action<object>>玩家等待特定命令的响应的列表(?)
Confusedbool玩家是否处于控制左右颠倒(已失效)
Countrystring玩家的国家代码(需要在Config开启EnableGeoIP)(无数据N/A, 代理A1)
CurrentRegionRegion玩家当前所处的区域
Deadbool玩家是否处于死亡状态
Difficultyint玩家的角色难度(软核0,中核1,硬核2,旅行3)
DisplayLogsbool是否向玩家发送服务器日志
GodModebool玩家是否已启用上帝模式(直接修改不影响玩家的无敌状态)
HasBeenNaggedAboutLoggingInbool玩家是否已经被提示过修改区域需要登录
IceTilesList<Point>玩家放置冰砖(冰杖)的点
IsDisabledForBannedWearablebool玩家是否由于携带禁用物品而被限制行动(Disable)
IsDisabledForSSCbool玩家是否由于没有登录以获取SSC数据而被限制行动(Disable)
IsDisabledForStackDetectionbool玩家是否由于恶意修改物品堆叠而被限制行动(Disable)
IsDisabledPendingTrashRemovalbool玩家是否由于在登录前在垃圾桶放置物品而被限制行动(Disable)
IsLoggedInbool玩家是否已经登录
ItemInHandItem玩家手持的物品
LastKilledProjectileint玩家上一次试图清除的最后一个弹幕
LastNetPositionVector2玩家最后更新时的图格坐标(TileXY)
lastPermissionWarninglong玩家上次被提示无权建筑警告的时间戳
LastPvPTeamChangeDateTime玩家上次切换队伍或PVP状态的时间
LastWhisperTSPlayer玩家上次进行耳语(私聊或被私聊)的对象
LoginHarassedbool玩家是否因为登录而被限制行动(?)
LoginMSlong玩家加入服务器时的时间戳
mutebool玩家是否处于禁言状态
PlayerDataPlayerData玩家的SSC人物数据
RecentFuseint玩家使用炸药的倒计时,即距离下次可用正常使用炸弹剩余时间(单位: 秒)
RecentlyCreatedProjectilesList<GetDataHandlers.ProjectileStruct>跟踪这个玩家最近创建的弹幕OnSecondUpdate() 在异步任务中删除这个。超过5秒的投射物将从这个集合中清除,因为它们不再是“最近的”。
RequestedSectionbool玩家是否已请求过获取地图区块
RequiresPasswordbool玩家是否被要求输入密码(加入服务器前)
RPPendingint玩家传送到上次离开服务器位置的倒计时(单位: 秒)
SilentJoinInProgressbool玩家是否静默加入服务器(不发送玩家加入服务器的提示)
SilentKickInProgressbool玩家是否静默踢出服务器(不发送玩家踢出服务器的提示)
sXint玩家出生点的X坐标(修改无效)
sYint玩家出生点的Y坐标(修改无效)
TeleportCoordsVector2不明,在TShock已弃用
tempGroupGroup玩家当前的临时组(会覆盖原来的组)
tempGroupTimerTimer玩家临时组有效期的计时器
TempPointsPoint[2]玩家设置的区域临时点
TPAllowbool玩家是否允许其他玩家对他使用TP类命令(tshock.tp.override忽略此限制)
  • TSPlayer类中的方法(几乎全要记住)
方法名参数返回值作用
Banstring reason(封禁理由), string adminUserName = null(管理用户名)bool(封禁是否成功)封禁玩家(通常使用TShock.Bans.InsertBan和Kick组合封禁玩家)
DamagePlayerint damage(造成伤害点数)void对玩家造成伤害(会造成击退)[如果你只是想扣除玩家血量,请直接修改他的生命值]
Disablestring reason = ""(理由), DisableFlags flags = DisableFlags.WriteToLog(日志记录模式)void限制玩家行动(石化玩家)
Disconnectstring reason(理由)void断开玩家连接
GiveItemint type(物品ID), int stack(物品堆叠), int prefix = 0(物品修饰语ID)void给予玩家物品
GiveItemCheckint type(物品ID), string name(物品全名[用于检测是否属于封禁物品]), int stack(物品堆叠), int prefix = 0(物品修饰语ID)bool(检查结果)判断玩家是否能被给予某物品
HasBuildPermissionint x(TileX), int y(TileY), bool shouldWarnPlayer = true(是否警告玩家)bool(检查结果)判断玩家是否能编辑此图格
HasBuildPermissionForTileObjectint x(TileX), int y(TileY), int width(宽度), int height(高度), bool shouldWarnPlayer = true(是否警告玩家)bool(检查结果)判断玩家是否能编辑以(X,Y)为左下角,宽度为width,高度为height的矩形(只要范围内有一个图格玩家不能编辑就会返回false)
HasHackedItemStacksbool shouldWarnPlayer = false(是否警告玩家)bool(检查结果)检测玩家是否作弊堆叠(例如:堆叠999999土块)
HasModifiedIceSuccessfullyint x(TileX), int y(TileY), short tileType(图格类型), GetDataHandlers.EditAction editAction(编辑操作类型)bool(检查结果)判断玩家是否成功编辑冰(冰杖生成的)
HasPaintPermissionint x(TileX), int y(TileY)bool(检查结果)判断玩家是否能为此图格涂漆
HasPermissionstring permission(权限)bool(检查结果)判断玩家是否拥有某个权限 (优先级如下: 1.权限钩子修改的结果 2.临时组的权限 3.玩家当前组的权限) [临时组和玩家组的权限不叠加]
Healint health = 600(治疗点数)void治疗一个玩家
IsBeingDisabled无bool(检查结果)判断玩家是否被限制行动
IsBouncerThrottled无bool(检查结果)判断玩家是否被Bouncer限制(?)
IsInRangeint x(TileX), int y(TileY), int range = 32(检查半径)bool(检查结果)判断玩家是否在以(X,Y)为圆点range为半径的圆内[当Config的RangeChecks被设为false后无论结果只返回true]
Kickstring reason(理由), bool force = false(强制踢出[无视tshock.admin.nokick权限]), bool silent = false(不发送广播), string adminUserName = null(管理员名), bool saveSSI = false(保存玩家SSC数据)bool(踢出结果)踢出一名玩家
KillPlayer无void杀死玩家(对上帝模式玩家无效)[原理是对玩家造成99999点伤害=DamagePlayer(99999)]
Logout无void使玩家登出
RemoveProjectileint index(弹幕ID), int owner(弹幕发射者索引)void移除一个弹幕(?)
SaveServerCharacter无bool(保存结果)保存玩家的SSC云存档(服务器没有打开SSC时总返回false)
SetBuffint type(增益ID), int time = 3600(持续帧[1秒=60帧]), bool bypass = false(忽略Bouncer对玩家的限制)void添加玩家BUFF(若玩家已有该BUFF且持续时间更长,你添加的BUFF会被忽略)[BUFF时间不会叠加]
SetPvPbool mode(true打开,false关闭), bool withMsg = false(是否广播PVP打开提示)void切换玩家PVP状态
SetTeamint team(无队伍0,红队1,绿队2,蓝队3,黄队4,粉队5)void切换玩家队伍(不发送广播)
SpawnPlayerSpawnContext context(玩家生成的原因), int? respawnTimer = null(重生倒计时[似乎无效])void复活一个玩家
Spawnint tilex, int tiley, PlayerSpawnContext context, int? respawnTimer = null, short? numberOfDeathsPVE = null, short? numberOfDeathsPVP = nullvoid复活玩家,大部分参数无效,不做解释
Teleportfloat x(X坐标), float y(Y坐标), byte style = 1(TP类型[不做解释])void将玩家传送到某一位置
Whoopieobject time(其实是个int,单位:秒)void发出烦人的声音 需要另开线程使用,否则会卡死主线程
TempGroupTimerElapsed不做解释不做解释不应该被Pubic的内部方法

*部分方法例如:SendDate另做介绍

  • 发送消息
字段名参数返回值作用/含义
SendMessagestring msg(内容), Color colorvoid发送自定义颜色消息
SendMessage/SendMessageFromPlayerstring msg(内容), byte red, byte green, byte blue(十进制颜色代码)void发送自定义颜色消息
SendInfoMessagestring msg(内容), params object[] argsvoid发送黄色提示消息
SendSuccessMessagestring msg(内容), params object[] argsvoid发送绿色成功消息
SendWarningMessagestring msg(内容), params object[] argsvoid发送橙色警告消息
SendErrorMessagestring msg(内容), params object[] argsvoid发送红色错误消息
SendFileTextAsMessagestring file(文件路径)void将文本文件内容发送给玩家 (支持替换: %map%:地图名 %players%:在线玩家 %specifier%:命令标识符 %onlineplayers%:在线玩家数 %serverslots%:最大玩家数)
SendMultipleMatchErrorIEnumerable<object> matches(不好解释)void发送多个匹配错误信息

效果:

  • SendMessage/SendInfoMessage/SendSuccessMessage/SendWarningMessage/SendErrorMessage
args.Player.SendMessage("我是SendMessage~", 255, 155, 15);
args.Player.SendInfoMessage("我是SendInfoMessage~,你的Index是{0}",args.Player.Index);
args.Player.SendSuccessMessage("我是SendSuccessMessage~,你现在在{0}地图游玩,所在路径是{1}",Main.worldName,Main.worldPathName);
args.Player.SendWarningMessage("我是SendWarningMessage~");
args.Player.SendErrorMessage("我是SendErrorMessage~");

image

  • SendFileTextAsMessage
args.Player.SendFileTextAsMessage("tshock/motd.txt"); //相对路径
//args.Player.SendFileTextAsMessage("C:/Users/13110/Desktop/code/TShock144/tshock/motd.txt"); //绝对路径
#motd.txt
欢迎加入[c/ffff00:%map%]在[c/7ddff8:T][c/81dbf6:S][c/86d7f4:h][c/8ad3f3:o][c/8ecef1:c][c/93caef:k]上运行 [c/55d284:T][c/62d27a:e][c/6fd16f:r][c/7cd165:r][c/89d15a:a][c/95d150:r][c/a4d145:i][c/b1d03b:a]服务器.
[c/FFFFFF:在线玩家 (%onlineplayers%/%serverslots%):] [c/FFFF00:%players%]
输入 [c/55D284:%specifier%][c/62D27A:h][c/6FD16F:e][c/7CD165:l][c/89D15A:p] 获取更多帮助信息.

image

  • SendMultipleMatchError
TSPlayer plr;
List<TSPlayer> plrs = TSPlayer.FindByNameOrID(args.Parameters[0]);
//args.Parameters[0]为玩家名字或索引
//如果参数为 tsi:2 则表示索引为2的玩家
//如果参数为 tsn:233 则表示名字为233的玩家
if (plrs.Count == 0)
{
    args.Player.SendErrorMessage("玩家不存在!"); //发送错误消息
    return;
}
else if (plrs.Count > 1)
{
    args.Player.SendMultipleMatchError(plrs.Select(p => $"{p.Name}({p.Index})")); //发送多个匹配结果错误
                                                                  //会向玩家发送:
                                                                  //找到多个匹配项-无法判断哪个是正确的:
                                                                  //玩家1(0),玩家2(1)....
                                                                  //用"半角双引号"包裹关键字来搜索名字带有空格的物品
                                                                  //使用 tsi:序号 或者 tsn:名称 来区分含有数字的名称.
    return;
}
else
{
    plr = plrs[0];
    args.Player.SendSuccessMessage("找到匹配项!");
    //找到匹配的玩家对象
}

image

image


Player对象

获取Player对象

  • 通过TSPlayer获取(推荐)
TSPlayer plr;
...(通过上面的方法找到TSPlayer对象)
Player tplr = plr.TPlayer;
  • 直接通过索引
Player tplr = Main.player[index];
  • 通过Where进行搜索(其实建议先找TSPlayer的...)

    这种方法用于寻找特定条件的玩家,是一种万能的方法,示例代码如下: (p!=null 很重要,请务必写上)

string name = "233"; //假设的量
Player tplr;

//var是推断类型的意思,使用var编译器会自己判断变量的类型,下面是一种偷懒的写法,完整的应该是IEnumerable<Player> plrs
var tplrs = Main.player.Where(p => p != null && p.name == name); //通过玩家名字获取玩家对象
if (tplrs.Count() == 0)
{
    //玩家对象不存在
    return;
}
else if (tplrs.Count() > 1)
{
    //玩家对象存在多个
    return;
}
else
{
    tplr = tplrs.First();
    //plr即为玩家对象
}

Player对象常用方法和字段

  • 生命/魔力
字段名类型作用/意义
statLifeint玩家当前生命值
statLifeMaxint玩家生命上限(不包括生命力药水、饰品等额外增加的生命)
statLifeMax2int玩家生命上限(包括生命力药水、饰品等额外增加的生命)[不可修改]
statManaint玩家当前魔力值
statManaMaxint玩家魔力上限(不包括水晶球、饰品等额外增加的魔力)
statManaMax2int玩家魔力上限(包括水晶球、饰品等额外增加的魔力)[不可修改]
  • 永久增益
字段名类型作用/意义
extraAccessorybool恶魔之心(增加饰品格位)
unlockedBiomeTorchesbool火把神徽章(环境火把)
ateArtisanBreadbool工匠面包(扩大制作站范围)
usedAegisCrystalbool生命水晶(永久强化生命再生)
usedAegisFruitbool埃癸斯果(永久提高防御力)
usedArcaneCrystalbool奥术水晶(永久提高魔力再生)
usedGalaxyPearlbool银河珍珠(永久增加运气)
usedGummyWormbool黏性蠕虫(永久提高钓鱼技能)
usedAmbrosiabool珍馐(永久提高采矿和建造速度)
unlockedSuperCartbool矿车升级包
  • 背包库存相关
字段名类型 (长度)作用/意义
inventoryItem[] (59)背包(0-49:背包 ; 50-53:钱币 ; 54-58:弹药)
trashItemItem垃圾桶物品
bankChest (item:40)猪猪储钱罐(bank.item)
bank2Chest (item:40)保险箱(bank2.item)
bank3Chest (item:40)护卫熔炉(bank3.item)
bank4Chest (item:40)虚空保险箱(bank4.item)
armorItem[] (20)装备栏 (0:头盔 ; 1:上衣 ; 2裤子 ; 3-9: 配饰) (10:时装头盔 ; 11:时装上衣 ; 12时装裤子 ; 13-19: 时装配饰)
dyeItem[] (10)染料
CurrentLoadoutIndexint当前玩家装备栏索引
LoadoutsEquipmentLoadout(3) (Armor:20;Dye:10)玩家装备栏,eg: Item[] armor = tplr.Loadouts[1].Armor;
  • 群系&环境相关
字段名类型环境/区域/群系
ZoneSkyHeightbool太空
ZoneOverworldHeightbool地表
ZoneDirtLayerHeightbool地下
ZoneRockLayerHeightbool洞穴
ZoneUnderworldHeightbool地狱
ZoneCorruptbool腐化之地
ZoneCrimsonbool猩红之地
ZoneHallowbool神圣之地
ZoneDesertbool沙漠
ZoneUndergroundDesertbool地下沙漠
ZoneJunglebool丛林(雨林)
ZoneLihzhardTemplebool丛林神庙
ZoneBeachbool沙滩
ZoneMarblebool大理石洞
ZoneShimmerbool微光
ZoneGraveyardbool墓地
ZoneDungeonbool地牢
ZoneMeteorbool陨石群系
ZoneGlowshroombool发光蘑菇地
ZoneGranitebool花岗岩洞
ZoneGemCavebool宝石洞
ZoneHivebool蜂巢
ZoneShadowCandlebool暗影蜡烛
ZonePeaceCandlebool和平蜡烛
ZoneWaterCandlebool水蜡烛
ZoneRainbool下雨
ZoneSnowbool雪地
ZoneSandstormbool沙尘暴
ZoneOldOneArmybool撒旦军队
ZoneTowerNebulabool星云柱
ZoneTowerSolarbool日耀柱
ZoneTowerStardustbool星尘柱
ZoneTowerVortexbool星旋柱
  • 其他
字段名类型作用/意义
ghostbool玩家幽灵状态
luckfloat玩家幸运值
anglerQuestsFinishedint玩家已完成渔夫任务数
golferScoreAccumulatedint玩家已经获得的高尔夫球分数
difficultybyte玩家角色难度(软核0,中核1,硬核2,旅行3)
SelectedItemint玩家当前手持物品的slot(背包索引)

习题:​

1.编写指令/dead,执行后返回当前死亡玩家列表(绿色成功消息)​

image

参考答案
using System.Reflection;
using Terraria;
using TerrariaApi.Server;
using TShockAPI;

namespace Plugin
{
    [ApiVersion(2, 1)]
    public class Plugin : TerrariaPlugin
    {
        //定义插件的作者名称
        public override string Author => "Cai";
        //插件的一句话描述
        public override string Description => "Player";
        //插件的名称
        public override string Name => "Player";
        //插件的版本
        public override Version Version => Assembly.GetExecutingAssembly().GetName().Version;


        //插件的构造器
        public Plugin(Main game) : base(game)
        {
        }

        //插件加载时执行的代码
        public override void Initialize()
        {
            Commands.ChatCommands.Add(new Command(Dead, "dead"));
        }

        private void Dead(CommandArgs args)
        {
            var plrs = TShock.Players.Where(p => p != null && p.Dead); //通过获取死亡的玩家对象
            if (plrs.Count() == 0)
            {
                //玩家对象不存在
                args.Player.SendSuccessMessage($"当前没有玩家死亡");
                return;
            }
            args.Player.SendSuccessMessage($"当前死亡玩家: {string.Join(',',plrs.Select(p=>p.Name))}");
        }


        //插件卸载时执行的代码
        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                var asm = Assembly.GetExecutingAssembly();
                Commands.ChatCommands.RemoveAll(c => c.CommandDelegate.Method?.DeclaringType?.Assembly == asm); //卸载命令
            }
            base.Dispose(disposing);
        }
    }
}

2.编写指令"/看看你 {玩家名} ",执行后返回玩家的 生命/生命最大值(魔力无加成最大值)、魔力/魔力最大值(魔力无加成最大值)、玩家的难度(旅行、软、中、硬核)、幸运值luck(绿色成功消息)​

image

参考答案
using System.Reflection;
using Terraria;
using TerrariaApi.Server;
using TShockAPI;

namespace Plugin
{
    [ApiVersion(2, 1)]
    public class Plugin : TerrariaPlugin
    {
        //定义插件的作者名称
        public override string Author => "Cai";
        //插件的一句话描述
        public override string Description => "Player";
        //插件的名称
        public override string Name => "Player";
        //插件的版本
        public override Version Version => Assembly.GetExecutingAssembly().GetName().Version;

        //插件的构造器
        public Plugin(Main game) : base(game)
        {
        }

        //插件加载时执行的代码
        public override void Initialize()
        {
            Commands.ChatCommands.Add(new Command(See, "看看你"));
        }

        private void See(CommandArgs args)
        {
            TSPlayer plr;
            if (args.Parameters.Count == 0)
            {
                args.Player.SendErrorMessage($"格式错误!正确格式:{TShock.Config.Settings.CommandSpecifier}看看你 玩家名");
                return;
            }
            List<TSPlayer> plrs = TSPlayer.FindByNameOrID(args.Parameters[0]);
            //args.Parameters[0]为玩家名字或索引
            //如果参数为 tsi:2 则表示索引为2的玩家
            //如果参数为 tsn:233 则表示名字为233的玩家
            if (plrs.Count == 0)
            {
                args.Player.SendErrorMessage("玩家不存在!"); //发送错误消息
                return;
            }
            else if (plrs.Count > 1)
            {
                args.Player.SendMultipleMatchError(plrs.Select(p => p.Name)); //发送多个匹配结果错误
                                                                              //会向玩家发送:
                                                                              //找到多个匹配项-无法判断哪个是正确的:
                                                                              //玩家1,玩家2....
                                                                              //用"半角双引号"包裹关键字来搜索名字带有空格的物品
                                                                              //使用 tsi:序号 或者 tsn:名称 来区分含有数字的名称.
                return;
            }
            else
            {
                plr = plrs[0];
                string difficult = "";
                switch (plr.Difficulty)
                {
                    case 3:
                        difficult = "旅行";
                        break;
                    case 0:
                        difficult = "软核";
                        break;
                    case 1:
                        difficult = "中核";
                        break;
                    case 2:
                        difficult = "硬核";
                        break;
                }
                args.Player.SendSuccessMessage($"{plr.Name}:");
                args.Player.SendSuccessMessage($"生命:{plr.TPlayer.statLife}/{plr.TPlayer.statLifeMax2}({plr.TPlayer.statLifeMax})");
                args.Player.SendSuccessMessage($"魔力:{plr.TPlayer.statMana}/{plr.TPlayer.statManaMax2}({plr.TPlayer.statManaMax})");
                args.Player.SendSuccessMessage($"难度:{difficult}");
                args.Player.SendSuccessMessage($"幸运值:{plr.TPlayer.luck}");
                //找到匹配的玩家对象
            }
        }
        //插件卸载时执行的代码
        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                var asm = Assembly.GetExecutingAssembly();
                Commands.ChatCommands.RemoveAll(c => c.CommandDelegate.Method?.DeclaringType?.Assembly == asm); //卸载命令
            }
            base.Dispose(disposing);
        }
    }
}

3.编写指令"/看看你的 {玩家名} ",执行后返回玩家猪猪储蓄罐的钱币详情​

image

参考答案
using System.Reflection;
using Terraria;
using Terraria.ID;
using TerrariaApi.Server;
using TShockAPI;

namespace Plugin
{
    [ApiVersion(2, 1)]
    public class Plugin : TerrariaPlugin
    {
        //定义插件的作者名称
        public override string Author => "Cai";

        //插件的一句话描述
        public override string Description => "Player";

        //插件的名称
        public override string Name => "Player";

        //插件的版本
        public override Version Version => Assembly.GetExecutingAssembly().GetName().Version;

        //插件的构造器
        public Plugin(Main game) : base(game)
        {
        }

        //插件加载时执行的代码
        public override void Initialize()
        {
            Commands.ChatCommands.Add(new Command(See, "看看你的"));
        }

        private void See(CommandArgs args)
        {
            TSPlayer plr;
            if (args.Parameters.Count == 0)
            {
                args.Player.SendErrorMessage($"格式错误!正确格式:{TShock.Config.Settings.CommandSpecifier}看看你的 玩家名");
                return;
            }
            List<TSPlayer> plrs = TSPlayer.FindByNameOrID(args.Parameters[0]);
            //args.Parameters[0]为玩家名字或索引
            //如果参数为 tsi:2 则表示索引为2的玩家
            //如果参数为 tsn:233 则表示名字为233的玩家
            if (plrs.Count == 0)
            {
                args.Player.SendErrorMessage("玩家不存在!"); //发送错误消息
                return;
            }
            else if (plrs.Count > 1)
            {
                args.Player.SendMultipleMatchError(plrs.Select(p => p.Name)); //发送多个匹配结果错误
                                                                              //会向玩家发送:
                                                                              //找到多个匹配项-无法判断哪个是正确的:
                                                                              //玩家1,玩家2....
                                                                              //用"半角双引号"包裹关键字来搜索名字带有空格的物品
                                                                              //使用 tsi:序号 或者 tsn:名称 来区分含有数字的名称.
                return;
            }
            else
            {
                plr = plrs[0];
                int copper = 0, silver = 0, gold = 0, platinum = 0;
                foreach (Item item in plr.TPlayer.bank.item)
                {
                    switch (item.netID)
                    {
                        case ItemID.CopperCoin:
                            copper += item.stack;
                            break;
                        case ItemID.SilverCoin:
                            silver += item.stack;
                            break;
                        case ItemID.GoldCoin:
                            gold += item.stack;
                            break;
                        case ItemID.PlatinumCoin:
                            platinum += item.stack;
                            break;
                    }
                }
                args.Player.SendSuccessMessage($"{plr.Name}的小猪储蓄罐硬币情况如下:");
                args.Player.SendSuccessMessage($"[i:{ItemID.PlatinumCoin}]{platinum}");
                args.Player.SendSuccessMessage($"[i:{ItemID.GoldCoin}]{gold}");
                args.Player.SendSuccessMessage($"[i:{ItemID.SilverCoin}]{silver}");
                args.Player.SendSuccessMessage($"[i:{ItemID.CopperCoin}]{copper}");
            }
        }

        //插件卸载时执行的代码
        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                var asm = Assembly.GetExecutingAssembly();
                Commands.ChatCommands.RemoveAll(c => c.CommandDelegate.Method?.DeclaringType?.Assembly == asm); //卸载命令
            }
            base.Dispose(disposing);
        }
    }
}

4.实现自动队伍,当玩家登录或加入世界,玩家有以下权限就切换对应的队伍​

权限队伍
team.red红队(1)
team.green绿队(2)
team.blue蓝队(3)
team.yellow黄队(4)
team.pink粉队(5)

钩子: TShockAPI.Hooks.PlayerHooks.PlayerPostLogin、ServerApi.Hooks.NetGreetPlayer
设置队伍: TSPlayer.SetTeam(int team) (无队伍0,红队1,绿队2,蓝队3,黄队4,粉队5)

参考答案
using System.Reflection;
using Terraria;
using Terraria.ID;
using TerrariaApi.Server;
using TShockAPI;
using TShockAPI.Hooks;


namespace Plugin
{
    [ApiVersion(2, 1)]
    public class Plugin : TerrariaPlugin
    {
        //定义插件的作者名称
        public override string Author => "Cai";


        //插件的一句话描述
        public override string Description => "自动队伍";


        //插件的名称
        public override string Name => "AutoTeam";


        //插件的版本
        public override Version Version => Assembly.GetExecutingAssembly().GetName().Version;


        //插件的构造器
        public Plugin(Main game) : base(game)
        {
        }


        //插件加载时执行的代码
        public override void Initialize()
        {
            PlayerHooks.PlayerPostLogin+=OnLogin;
            ServerApi.Hooks.NetGreetPlayer.Register(this,OnGreetPlayers);
        }


        private void OnGreetPlayers(GreetPlayerEventArgs args)
        {
            CheckTeam(TShock.Players[args.Who]);
        }


        private void OnLogin(PlayerPostLoginEventArgs e)
        {
           CheckTeam(e.Player);
        }


        private void CheckTeam(TSPlayer plr)
        {
            if (plr.HasPermission("*"))
            {
                return;
            }
            int team = 0;
            int count = 0;
            if (plr.HasPermission("team.red"))
            {
                team = 1;
                count++;
            }
            if (plr.HasPermission("team.green"))
            {
                team = 2;
                count++;
            }
            if (plr.HasPermission("team.blue"))
            {
                team = 3;
                count++;
            }
            if (plr.HasPermission("team.yellow"))
            {
                team = 4;
                count++;
            }
            if (plr.HasPermission("team.pink"))
            {
                team = 5;
                count++;
            }
            if (count == 0)
            {
                plr.SendWarningMessage("[自动队伍]你所处的玩家组没有配置自动队伍!");
                return;
            }
            if (count > 1)
            {
                plr.SendErrorMessage("[自动队伍]你所处的玩家组配置了多个队伍!");
                return;
            }
            plr.SetTeam(team);
        }


        //插件卸载时执行的代码
        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                PlayerHooks.PlayerPostLogin -= OnLogin;
                ServerApi.Hooks.NetGreetPlayer.Deregister(this, OnGreetPlayers);
            }
            base.Dispose(disposing);
        }
    }
}

5.当玩家在墓地环境移动时,通过TSPlayer.DamagePlayer()对其造成5点伤害

钩子: GetDataHandlers.PlayerUpdate

参考答案
using System.Reflection;
using Terraria;
using Terraria.ID;
using TerrariaApi.Server;
using TShockAPI;
using TShockAPI.Hooks;

namespace Plugin
{
    [ApiVersion(2, 1)]
    public class Plugin : TerrariaPlugin
    {
        //定义插件的作者名称
        public override string Author => "Cai";

        //插件的一句话描述
        public override string Description => "墓地";

        //插件的名称
        public override string Name => "ZoneGraveyard";

        //插件的版本
        public override Version Version => Assembly.GetExecutingAssembly().GetName().Version;

        //插件的构造器
        public Plugin(Main game) : base(game)
        {
        }

        //插件加载时执行的代码
        public override void Initialize()
        {
            GetDataHandlers.PlayerUpdate.Register(OnPlayerUpdatePhysics);
        }

        private void OnPlayerUpdatePhysics(object? sender, GetDataHandlers.PlayerUpdateEventArgs e)
        {
            if (e.Player.TPlayer.ZoneGraveyard)
            {
                e.Player.DamagePlayer(5);
            }
        }


        //插件卸载时执行的代码
        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                GetDataHandlers.PlayerUpdate.UnRegister(OnPlayerUpdatePhysics);
            }
            base.Dispose(disposing);
        }
    }
}
上一篇
Part 4.挂钩钩~
下一篇
Part 6.0 数据包基础