1. Этот сайт использует файлы cookie. Продолжая пользоваться данным сайтом, Вы соглашаетесь на использование нами Ваших файлов cookie. Узнать больше.
  2. Вы находитесь в сообществе Rubukkit. Мы - администраторы серверов Minecraft, разрабатываем собственные плагины и переводим на различные языки плагины наших коллег из других стран.
    Скрыть объявление
Скрыть объявление
В преддверии глобального обновления, мы проводим исследования, которые помогут нам сделать опыт пользования форумом ещё удобнее. Помогите нам, примите участие!

Помогите Асинхронная отправка пакетов

Тема в разделе "Разработка плагинов для новичков", создана пользователем BeYkeR, 29 июн 2014.

  1. Автор темы
    BeYkeR

    BeYkeR Старожил Девелопер Пользователь

    Баллы:
    173
    После нескольких часов работы над минилизацией лагов для LightSource(при отправке пакета, сервер начинает сильно тормозить, или компьютер такой) у меня появилась одна мысль: Возможно ли отправить пакеты по асинхронному потоку ?
     
  2. fromgate

    fromgate Старожил Пользователь

    Баллы:
    173
    Имя в Minecraft:
    fromgate
    А что такое LightSource?
     
  3. kirill2011s

    kirill2011s Старожил Пользователь

    Баллы:
    103
    Вы занимались когда-нибудь клиент-серверным программированием?

    Если нет, то вот вам ответ: весь ваш канал интернет-связи, однопоточен. Чем шире у вас канал, тем быстрее данные передаются. Но многопоточности тут и речи быть не может. Все данные выстраиваются в очередь и отправляются тоже по очереди.
     
  4. ptnk

    ptnk Старожил Пользователь

    Баллы:
    173
    Можно ссылку на источник данной глупости? Здесь автор говорит про прикладной уровень, когда в любой другой программе можно из нескольких потоков отправлять различную информацию, а ты говоришь уже про уровень железа и про уровень операционной системы, когда идёт формирование пакетов на отправку.
     
  5. fromgate

    fromgate Старожил Пользователь

    Баллы:
    173
    Имя в Minecraft:
    fromgate
    @ptnk
    Ну если по теме, Вы наверняка, в курсе. Сейчас насколько я понимаю, нет смысла озадачиваться отправкой пакетов в асинхронном потоке - это уже по идее выполняется самим сервером. По крайней мере, как-то Диннербон, что-то об этом говорил. Или нет? Я как-то так глубоко не копал.

    @BeYkeR
    Вообще, если говорить "на бытовом уровне", то как-то в моем плагине на ограничение террритории (аналог Borders, BorderGuard и т.п.) решил по границе нарисовать дымки. Т.е. каждые несколько секунд от сервера клиентам (всем игрокам) отправлялось несколько тысяч пакетов на отображение эффекта дыма.
    Тормоза были. Но именно на уровне сети. Т.е. я банально перегрузил канал сервера, при этом TPS ни разу не упал.
     
  6. kirill2011s

    kirill2011s Старожил Пользователь

    Баллы:
    103
    Через канал интернета всегда была отправка однопоточной. Все данные делятся на пакеты и отправляются. Если вы считаете что существует многопоточность, то это лишь иллюзия, пакеты могут добавляться асинхронно.
    В любом случае, если уж говорить о майнкрафте, то клиент как принимал все в 1 потоке, так и будет принимать.
     
  7. ptnk

    ptnk Старожил Пользователь

    Баллы:
    173
    Если у меня есть канал 100МБит, значит считается, что я смогу отправить одновременно столько данных, если у клиенте есть столько - значит он столько сможет получить одновременно.

    И мне, как java - программисту глубоко всё равно, как операционная система оперирует с каналом. У меня в секунду отправляется столько-то пакетов, за 1ms - столько-то данных, поэтому не нужно пудрить людям мозги с очередью и загружать их не нужной информации.
    Любые данные одновременно отправленные из разных потоков придут к одному и тому же получателю с минимальной разницей.
     
  8. kirill2011s

    kirill2011s Старожил Пользователь

    Баллы:
    103
    Хорошо, не буду пудрить никому мозг. Возможно, либо вы меня сначала не правильно поняли, либо я не очень понял автора.
     
  9. gamerforEA

    gamerforEA Старожил Пользователь

    Баллы:
    143
    Skype:
    sk2000sk1
    Имя в Minecraft:
    gamerforEA_MCPC
    Задача: обмен данными в отдельных потоках с целью снижения нагрузки на основной поток.
     
  10. Shevchik

    Shevchik Старожил Пользователь

    Баллы:
    173
    Имя в Minecraft:
    _Shevchik_
    Тормозить при отправке пакета? Нетворк итак в отдельных потоках выполняется.
    Ваша проблема точно не в отправке пакета.
     
  11. Автор темы
    BeYkeR

    BeYkeR Старожил Девелопер Пользователь

    Баллы:
    173
    BukkitDev
    Написал -Lite версию плагина:
    Код:
    package ykt.BeYkeRYkt.LightSourceLite;
    
    import java.util.ArrayList;
    import java.util.List;
    
    import net.minecraft.server.v1_7_R2.Chunk;
    import net.minecraft.server.v1_7_R2.EnumSkyBlock;
    import net.minecraft.server.v1_7_R2.PacketPlayOutMapChunk;
    import net.minecraft.server.v1_7_R2.WorldServer;
    
    import org.bukkit.Bukkit;
    import org.bukkit.Location;
    import org.bukkit.Material;
    import org.bukkit.block.Block;
    import org.bukkit.block.BlockFace;
    import org.bukkit.craftbukkit.v1_7_R2.CraftChunk;
    import org.bukkit.craftbukkit.v1_7_R2.CraftServer;
    import org.bukkit.craftbukkit.v1_7_R2.CraftWorld;
    import org.bukkit.entity.Player;
    import org.bukkit.plugin.java.JavaPlugin;
    
    public class LSL extends JavaPlugin{
    
        public int task = 0;
        private static BlockFace[] SIDES = { BlockFace.UP, BlockFace.DOWN,
            BlockFace.NORTH, BlockFace.EAST, BlockFace.SOUTH, BlockFace.WEST };
    
        private ArrayList<String> players = new ArrayList<String>();
    
        @Override
        public void onDisable() {
            getLogger().info("Disabled!");
            stopSyncTask();
        }
    
        @Override
        public void onEnable() {
            getLogger().info("Enabled!");
            startSyncTask();
        }
    
        public void stopSyncTask() {
            Bukkit.getScheduler().cancelTask(task);
        }
    
    
        public Block getAdjacentAirBlock(Block block) {
            for (BlockFace face : SIDES) {
                if (block.getY() == 0x0 && face == BlockFace.DOWN)
                    continue;
                if (block.getY() == 0xFF && face == BlockFace.UP)
                    continue;
    
                Block candidate = block.getRelative(face);
    
                if (candidate.getType().isTransparent()) {
                    return candidate;
                }
            }
            return block;
        }
    
        public void startSyncTask(){
            task = Bukkit.getScheduler().runTaskTimer(this, new Runnable(){
                @Override
                public void run(){
                    final List<String> playernames = new ArrayList<String>();
                    for (int p = 0; p < Bukkit.getOnlinePlayers().length; p++) {
                        playernames.add(Bukkit.getOnlinePlayers()[p].getName());
                    }
                    Player player = null;
                    for (final String playername : playernames) {
                        player = Bukkit.getPlayer(playername);
                        if (player != null) {
                            if (player.getInventory().getItemInHand().getType() == Material.TORCH) {
                            if(players.contains(player.getName())){
                              for(int x=-2; x <=2; x++){
                                for(int y=-2; y<=2; y++){
                                  for(int z=-2; z<=2; z++){
                                    Location loc = new Location(player.getWorld(), player.getLocation().getBlockX() + x, player.getLocation().getBlockY() + y, player.getLocation().getBlockZ() + z);
                                   resetLight(loc);
                                  }
                                }
                              }
                                    lightUp(player.getLocation(), 14);
                                    sendSyncPackets(player.getLocation());
                            }else{
                                players.add(player.getName());
                            }
                                }else{
                                if(players.contains(player.getName())){
                                for(int x=-2; x <=2; x++){
                                 for(int y=-2; y<=2; y++){
                                    for(int z=-2; z<=2; z++){
                                        Location loc = new Location(player.getWorld(), player.getLocation().getBlockX() + x, player.getLocation().getBlockY() + y, player.getLocation().getBlockZ() + z);
                                        resetLight(loc);
                                    }
                                 }
                                }
                                   //resetLight(player.getLocation());
                                    sendSyncPackets(player.getLocation());
                                    players.remove(player.getName());
                                }
                                }
                       
                        }
                    }
                }
            }, 0L, 7L).getTaskId();
        }
    
    
        public void lightUp(Location loc, int level) {
            WorldServer nmsWorld = ((CraftWorld) loc.getWorld()).getHandle();
       
            int x = loc.getBlockX();
            int y = loc.getBlockY();
            int z = loc.getBlockZ();
       
            nmsWorld.b(EnumSkyBlock.BLOCK, x, y, z, level);
        }
    
        public void resetLight(Location loc) {
            WorldServer nmsWorld = ((CraftWorld) loc.getWorld()).getHandle();
       
            int x = loc.getBlockX();
            int y = loc.getBlockY();
            int z = loc.getBlockZ();
       
            nmsWorld.c(EnumSkyBlock.BLOCK, x, y, z);   
        }
    
        public void sendSyncPackets(Location loc) {
            for(int x=-1; x<=1; x++) {
                for(int z=-1; z<=1; z++) {
                    long startTime = System.currentTimeMillis();
                    //Send       
                    Chunk chunk = ((CraftChunk)loc.clone().add(16 * x, 0, 16 * z).getChunk()).getHandle();
                    PacketPlayOutMapChunk packet = new PacketPlayOutMapChunk(chunk, false, '\uffff');
               
                    Block adjacent = getAdjacentAirBlock(loc.getBlock());
                    ((CraftWorld) loc.getWorld()).getHandle().t(adjacent.getX(), adjacent.getY(),adjacent.getZ());
                    ((CraftServer) Bukkit.getServer()).getServer().getPlayerList().sendPacketNearby(loc.getX(), loc.getY(), loc.getZ(), 64, ((CraftWorld) loc.getWorld()).getHandle().dimension, packet);
                    chunk.initLighting();
                    long endTime = System.currentTimeMillis();
                    getLogger().info("Sync: " + (endTime - startTime) + " ms!");
                }
            }
        }
    }
    
    
    Результат показывает ~10 - 20 миллисекунд. Со временем TPS падает до ~11 и держится там до тех пор, пока игрок не уберет факел. Во всех значениях period'a. Мне сейчас важно не вызывать частые лаги при использовании плагина.
     
    Последнее редактирование: 30 июн 2014
  12. kirill2011s

    kirill2011s Старожил Пользователь

    Баллы:
    103
    Спасибо, я уже понял.
     
  13. Shevchik

    Shevchik Старожил Пользователь

    Баллы:
    173
    Имя в Minecraft:
    _Shevchik_
    Ваша проблема не в отправке пакета а в том что вы выполняете сложный расчётный таск каждый тик.
    Вы видели сходный код метода World.c(EnumSkyBlock....)? Так вот он:
    https://github.com/Bukkit/CraftBukk...in/java/net/minecraft/server/World.java#L2174
    А ещё и chunk.initLightning().
    https://github.com/Bukkit/CraftBukkit/blob/master/src/main/java/net/minecraft/server/Chunk.java#L174
    Неудивительно что у вас тпс адово проседает.
     
  14. Автор темы
    BeYkeR

    BeYkeR Старожил Девелопер Пользователь

    Баллы:
    173
    Да уж, уже начинаю забывать смотреть на исходный код).
    А что если я буду использовать старый метод вместо nmsWorld.c():
    Код:
    Location delete = new Location(loc.getWorld(), x, y, z);
    Material blockMaterial = delete.getBlock().getType();
    byte blockData = delete.getBlock().getData(); //Хотя зачем мне это ?
    delete.getBlock().setType(blockMaterial);
    delete.getBlock().setData(blockData);
    и уберу chunk.initLightning() , TPS повысится ?
     

Поделиться этой страницей