CarbonPaper99
主要就是Player JoinEvent 然后计时器开启,当计数值大于100 ticks时调动插件运行传送指令
但计时器不会写,是一次性的那种,用完就注销。

吕易天
本帖最后由 吕易天 于 2019-8-17 14:38 编辑

Player p=e.getPlayer();
new Thread(()->{
for(int i=0;i<100;i++)
{
for(int i2=0;i2<50;i++)
{
if(!p.isOnline())
  return;
try{
Thread.sleep(1);
}catch(Throwable e2){}
}
}
if(!p.isOnline())
  return;
Bukkit.getScheduler().runTask(this,()->{
Bukkit.dispatchCommand(p,"命令(不带/)");
});
}).start();




Dante_7
吕易天 发表于 2019-8-17 14:26
Player p=e.getPlayer();
new Thread(()->{
for(int i=0;i{

就这种答案也好意思往上摆? 误人子弟吗?

有两种方式: 使用 Bukkit 自带的计时器(BukkitRunnable) 或是 Java 提供的线程池: ScheduledExecutorService

我个人推荐你用第二种(因为我比较熟悉).

具体的代码怎么写, 还需要你自己去翻阅文档, 因为这两种方式都没什么技术含量, 百度一下这两个词就有大把多的教程

BukkitRunnable

ScheduledExecutorService

另外别用楼上提供的方法, 这种代码放服务器上简直就是害人

另外要是我的答案对你有点用不给金粒也给个人气呗~


吕易天
凋灵兔子 发表于 2019-8-17 14:38
就这种答案也好意思往上摆? 误人子弟吗?

有两种方式: 使用 Bukkit 自带的计时器(BukkitRunnable) 或是 J ...

我要是用Bukkit内置的那玩意玩家离线了也会继续等

Dante_7
吕易天 发表于 2019-8-17 14:39
我要是用Bukkit内置的那玩意玩家离线了也会继续等

不好意思, Bukkit 内置的计时器也不是你这种用法

吕易天
凋灵兔子 发表于 2019-8-17 14:40
不好意思, Bukkit 内置的计时器也不是你这种用法

你的意思就是runTaskLater嘛,如果用runTaskLater会导致等待这100tick的时间内如果玩家离线又重进就会出现两个重复的task

吕易天
本帖最后由 吕易天 于 2019-8-17 14:55 编辑
凋灵兔子 发表于 2019-8-17 14:40
不好意思, Bukkit 内置的计时器也不是你这种用法

你的意思:
Bukkit.getScheduler().runTaskLater(this,()->Bukkit.dispatchCommand(e.getPlayer(),"命令"),100);
这种方法有问题,所以我才没用

Dante_7
吕易天 发表于 2019-8-17 14:49
你的意思:
Bukkit.getScheduler().runTaskLater(this,()->{Bukkit.dispatchCommand(e.getPlayer(),"命令" ...

我已经说过了, 我个人习惯用第二种. 第一种是否存在问题我不知道, 但是第二种一定是安全的.

插件属于服务端程序, 再写服务端程序的时候我都是慎之又慎, 特别是涉及到一些敏感资源比如数据库, IO, 线程 等. 因为这些资源如果使用不当, 在服务端这种同时为很多人工作的场景下, 一个很小的问题乘以一百个用户就是一个很大的问题, 而你的解决方式就是用 new Thread 的方式新建一个线程来计时吗? 如果有一百个玩家使用这个功能, 我就要创建一百个线程吗? 有一万个玩家在线我要为这么小的一个功能而耗尽所有服务器资源创建一万个线程吗?

吕易天
本帖最后由 吕易天 于 2019-8-17 22:37 编辑
凋灵兔子 发表于 2019-8-17 14:57
我已经说过了, 我个人习惯用第二种. 第一种是否存在问题我不知道, 但是第二种一定是安全的.

插件属于服 ...

那么我还有一个方法:
类开头加:
public ConcurrentHashMap<String.Long> waitList=new ConcurrentHashMap<String,Long>();
public boolean terminate=false;
onEnable写:
Bukkit.getPluginManager().registerEvents(this,this);
Thread a=new Thread(()->{
while(!terminate)
{
Player[] temp=new Player[Bukkit.getOnlinePlayers().size()];
Bukkit.getOnlinePlayers().toArray(temp);
for(Player i : temp)
{
if(waitList.containsKey(i.getUniqueID().toString()))
{
if(waitList.get(i.getUniqueID().toString())<=0)
{
waitList.remove(i.getUniqueID().toString());
Bukkit.getScheduler().runTask(this,()->Bukkit.dispatchCommand(i,"命令"));
}else{
waitList.replace(i.getUniqueID().toString(),waitList.get(i.getUniqueID().toString())-1);
}
}try{
Thread.sleep(0,1);
}catch(Throwable e){}
}
try{
Thread.sleep(1);
}catch(Throwable e){}
}
});
a.setDaemon(true);
terminate=false;
a.start();
onDisable写:
waitList.clear();
terminate=true;
然后:
@EventHandler(priority=EventPriority.MONITOR,ignoreCancelled=false)
public void onPlayerJoin(PlayerJoinEvent e)
{
waitList.put(e.getPlayer().getUniqueID().toString(),5000);
}
@EventHandler(priority=EventPriority.MONITOR,ignoreCancelled=false)
public void onPlayerLeave(PlayerQuitEvent e)
{
waitList.remove(e.getPlayer().getUniqueID().toString());
}


Dante_7
吕易天 发表于 2019-8-17 15:12
那么我还有一个方法:
类开头加:
public ConcurrentHashMap waitList=new ConcurrentHashMap();

如果这就是你的真实技术的话, 我建议你还是下架你所有的插件吧.

1139365029
本帖最后由 1139365029 于 2019-8-17 15:49 编辑

看了一下回复,
考虑到假人压测和玩家有加入后立即退出的可能,
至少需要这些要求:线程不能开太多,也不能不需要时一直开着,玩家退出后不需要继续执行,
此外还需要额外满足:对玩家/服务器/指令等的操作必须在主线程,但其它操作不需要,

简单的说一下思路,
首先需要一个List或者类似的东西,
玩家加入时,将玩家和时间记录进去,
玩家退出时,或者执行操作之后,将记录移除,
其次还需要一个线程,用于遍历记录,并进行操作,
全部操作完毕之后,线程可以退出,
最后,玩家加入时,除了记录之外,
若线程未启动,则启动,反之不用管。

代码(未测试,大佬勿喷):
忘记线程安全了,重新改了一下,把操作List的代码放到了主线程,感谢@吕易天 的提醒


吕易天
1139365029 发表于 2019-8-17 15:32
看了一下回复,
考虑到假人压测和玩家有加入后立即退出的可能,
至少需要这些要求:线程不能开太多,也不能 ...

由于ArrayList是非线程安全的,所以我用了ConcurrentHashMap

Dante_7
吕易天 发表于 2019-8-17 15:37
由于ArrayList是非线程安全的,所以我用了ConcurrentHashMap

五秒钟内能上线几个人? 你就是这样把一个系统的稳定性寄托于运行时玩家登录多少吗? 好, 我告诉你, 我要压测一个服务器, 一秒钟我就能登录 1000 个人, 你的垃圾代码, 就是整个系统的突破口.

如果你就是这种不负责任的态度, 我问你一句: 就你也配写插件?

吕易天
本帖最后由 吕易天 于 2019-8-17 17:06 编辑
凋灵兔子 发表于 2019-8-17 16:56
五秒钟内能上线几个人? 你就是这样把一个系统的稳定性寄托于运行时玩家登录多少吗? 好, 我告诉你, 我要压 ...

我就是过于负责所以才写出了那种代码,我要真不负责我就直接Bukkit.getScheduler().runTaskLater(this,()->Bukkit.dispatchCommand(e.getPlayer(),"命令"),100);

吕易天
凋灵兔子 发表于 2019-8-17 16:56
五秒钟内能上线几个人? 你就是这样把一个系统的稳定性寄托于运行时玩家登录多少吗? 好, 我告诉你, 我要压 ...

如果是你你会怎么写?

Dante_7
吕易天 发表于 2019-8-17 17:04
我就是过于负责所以才写出了那种代码

哦呦, 牛逼啊大神, 过于负责, 所以写出能让别人轻易打垮服务器的代码, 大神负责的方式真是和我等彩笔有所不同哦.

你还说然而有其他机制确保, 请问其他机制是什么? 你的意思是你的软件的稳定性需要其他 "机制" 来保证? 不好意思, 我的软件的安全性应该是我负责, 不应该把这部分责任, 就因为你不想负责就推卸给其他 "机制", 照你这么说以后别人要删除一个插件还要看你插件的脸色, 万一你的插件用了这个机制, 我就不能删, 是吧?

再说了, ConcurrentHashMap 是你那么用的吗? 你怕是根本就没学过并发编程吧, 你以为 new 个 thread 就叫并发了? 赶紧好好学习学习吧.

不回复你了, 装睡的人叫不醒就不叫了.

吕易天
凋灵兔子 发表于 2019-8-17 17:09
哦呦, 牛逼啊大神, 过于负责, 所以写出能让别人轻易打垮服务器的代码, 大神负责的方式真是和我等彩笔有所 ...

我什么时候说我那是并发了。。。

吕易天
凋灵兔子 发表于 2019-8-17 17:09
哦呦, 牛逼啊大神, 过于负责, 所以写出能让别人轻易打垮服务器的代码, 大神负责的方式真是和我等彩笔有所 ...

还有,如果没有其它机制确保,就算没有我的插件,服务器也会爆炸,只是快一点慢一点的问题

w1453656098
学习 学习 学习

星辰sk
本帖最后由 2280761425 于 2019-8-20 14:40 编辑

如果是cd可以用System.currentTimeMillis()获取当前时间(单位是毫秒)就好,不用开线程这么麻烦延时的话还是用bukkit提供的开延时方法吧(玩家退出取消就行)

吕易天
2280761425 发表于 2019-8-18 18:45
其实用System.currentTimeMillis()获取当前时间(单位是毫秒)就好,不用开线程这么麻烦 ...

我觉得你还可以用System.nanoTime(),精确到了纳秒