Eida.cz - Vzkazy na mapě světa

Vzkazy na mapě světa

Eida

Už přes měsíc žijeme konečně v novém světě, na nové planetě, na optimalizovaném hardware a konečně i úplně nové infrastruktuře. Příchod 1.14 byl proti původnímu očekávání teda hodně zpožděný, až to skoro pěkné nebylo. A když už to pěkné bylo, objevilo se značné množství nepříjemných bugů, které prakticky znemožňovaly užít si celý původní plán. Sice i teď se jich několik našlo, ale zatím jsme je nepostřehli, takže asi 1.14.2 se konečně jeví jako použitelná verze.

Samozřejmě každý svět potřebuje nějaký svůj charakter, ale co si budeme lhát, i přes velké množství a variace biomů jsou všechny seedy těch nových vydání dost na jedno brdo a není moc co objevovat. Jakože jasně, nějaká ta skalka a pohled do dálky vždy potěší, ale nestává se to často. Pokud by to nějaké cizáky zajímalo, na počest naší nejkočičáčtější céry jedeme na seedu RulerkaInka, který je celkem schopný díky tomu, že zase absolutní novinky — pandy a bamby — jsou relativně blízko. Stejně jako v minulém životě 1.8 byla prakticky za oknem pestrobarevná mesa. Ale k ničemu to nebylo.

Vesnice u bambusového lesa

Jakmile se překonaly první technické nesnáze, mohli jsme začít opět taputukancky žít. Zatím ještě nepřišli všichni, což je trochu mrzuté, ale snad to tolik nevadí. Poučeni minulými nesnázemi jsme začali budovat každý dostatečně daleko. To je moc dobře kvůli tomu, že si pak každý udrží nějaký svůj styl a bude prostor pro inspirace. Smutné je, že plánovanou vesnici v džungli hned na začátku hrozným způsobem zmršili ssocka s LadyMislerei, ale takový je život, chunky jim za to mazat nebudeme. Když to vezmu z vlastního pohledu, podle zkušeností z prvního Kocíkového světa jsem obsadil divokou tajgu hezky u vody, což je ideální místo na kutbu, přestože se někomu třeba může zdát drsné a neobyvatelné.

Vtip je bohužel tak trochu v tom, že je celé srdce základny umístěné v podzemí, takže moc nevyčnívá, ale drží dekorativní linku tajgy s pomocí smrkového dřeva. Do navrhované velikosti se bezproblémově vešly obě Dýně & Co. farmy a šestistupňová vypalovací pec s překvapením, základní úložiště materiálu a pracovní koutky. No a v rámci tradic z bývalého Kočičího tunelu byl položen první 1500-dlouhý základ Kitty Train Tunnelu s řízenou dvoulinkovou železnicí, která snad nějak půjde napojit i na Chobotovy jízdní systémy. Jeho původním účelem bylo ale hlavně vytvoření bezpečného a stabilního spojení ke králíkové víle, abychom nemuseli zdlouhavě chodit přes nástrahy vnějšího světa, jelikož mezi námi leží tajemné a neprozkoumané skály Kocíkovských jeskyní.

Jelikož používáme Spigot, bez kterého by to nešlo, vzniká prostor pro využití nějakých decentních pluginů. Cílem rozhodně není změnit celý svět k nepoznání, ale spíš zanést nějaké užitečné nástroje pro správu a zajištění kontinuity času. To nejzajímavější, co by asi každý ale měl mít, je přístup k interaktivním mapám současného stavu celého světa. Jakkoliv úžasně to zní, už velmi dlouhou dobu je pro takové hrátky k dispozici plugin Dynmap, který ale pořád oficiálně nepřešel na podporu 1.14. Samozřejmě ale existuje nějaká průběžná vývojová větev, kterou si každý může libovolně nasosat a sestavit.

dynmap-build.txt 114 bajtů
$ git clone --single-branch --branch v3.0 https://github.com/webbukkit/dynmap.git
$ ./gradlew clean build install
Základní získání a sestavení Dynmapy 3 z repozitáře

Nebudu tady popisovat všechny možnosti pluginu, ale jednou z nejzajímavějších je rozhodně využití interaktivních značek pomocí speciálně formátovaných cedulí, tedy jakýchsi vzkazů na mapě světa. Logika je tedy taková, že prostě přijdeme na zajímavé místo, plácneme tam ceduli s popiskem a ona se sama objeví na mapě. Jednoduché jako nic. Bohužel v 1.14 tohle tak úplně nefunguje, protože cedulové rozšíření pracuje pouze s numericky číslovanými bloky a neumí zpracovat specializované značky. TLDR, takže ve výchozím stavu to bude celé fungovat pouze se stojatými dubovými cedulemi. Vzhledem k novým a pěkným barvičkám je to ale nežádoucí stav.

Fungovat to má zhruba tak, že umístění speciálně formátované cedule vytvoří nový marker ve zvoleném setu. Dosud v pořádku. Problém nastane s mechanikou zneplatňování takových cedulí. Její logika je v tom, že při každé změně v nějakém chunku proběhne kontrola všech odpovídajících markerů zapsaných speciálně v YAMLu a jejich ověření proti bloku, na kterém by měla být cedule. Pokud už není (tj. ceduli někdo rozbil), marker by se měl sám odstranit. Jenže se ukázalo, že při jakékoliv změně… se smažou všechny markery v oblasti.

Nehezké, takže přišla myšlenka se na to trochu podívat a pokusit se to znovu rozjet. Bohužel takhle narychlo spíchnutá oprava není úplně na odeslání do upstreamu, jelikož přepisuje vlastně celý Spigot pouze pro 1.14+. Základní konflikt je v tom, že iterační prohledávač a mazač ověřuje dané bloky proti pevně zapsaným magickým ID číslům stojaté a připnuté cedule — no a nové bloky logicky ID nemají, takže vrátí hodnotu -1, ta se ověří jakože to není žádná cedule a marker je následně odstraněn. Vize ověření by mohla vypadat asi takhle, aby nám interface pluginu řeklo, že daný blok není cedule v jakékoliv podobě.

--- dynmap-orig/DynmapCore/src/main/java/org/dynmap/markers/impl/MarkerSignManager.java	2019-06-05 00:35:46.000000000 +0200
+++ dynmap/DynmapCore/src/main/java/org/dynmap/markers/impl/MarkerSignManager.java	2019-06-05 12:26:14.000000000 +0200
@@ -19,9 +19,6 @@
     private static MarkerSignManager mgr = null;
     private static DynmapCore plugin = null;
     private static String defSignSet = null;
-
-    private static final int SIGNPOST_ID = 63;
-    private static final int WALLSIGN_ID = 68;

     private static class SignRec {
         String wname;
@@ -148,12 +145,11 @@
                     iter.remove();
                 }
                 else {
-                    /* Get block ID */
-                    int blkid = plugin.getServer().getBlockIDAt(r.wname, r.x, r.y, r.z);
-                    if((blkid >= 0) && (blkid != WALLSIGN_ID) && (blkid != SIGNPOST_ID)) {
+                    // cokoliv, co NENÍ cedule
+                    if (!plugin.getServer().isSignAt(r.wname, r.x, r.y, r.z)) {
                         r.m.deleteMarker();
                         iter.remove();
-                    }
+                    }
                 }
             }
             plugin.getServer().scheduleServerTask(sl, 60*20);
MarkerSignManager.java

Což je ale hrozně naivní, jelikož se taková metoda musí skutečně v rozhraní vyskytnout, jako proč ne, zápis je hrozně snadný, že jo.

--- dynmap-orig/DynmapCore/src/main/java/org/dynmap/common/DynmapServerInterface.java	2019-06-05 00:35:46.000000000 +0200
+++ dynmap/DynmapCore/src/main/java/org/dynmap/common/DynmapServerInterface.java	2019-06-04 23:03:41.000000000 +0200
@@ -170,6 +170,17 @@
     }
 
     /**
+     * Je objekt na souřadnicích cedulí?
+     *
+     * @param worldName
+     * @param x
+     * @param y
+     * @param z
+     * @return
+     */
+    public abstract boolean isSignAt(String worldName, int x, int y, int z);
+
+    /**
      * Get block ID at given coordinate in given world (if chunk is loaded)
      * @param wname - world name
      * @param x - X coordinate
DynmapServerInterface.java

No a teď ta největší legrace, konkrétní plugin, kterým je instance kompatibilního Bukkitu, musí danou metodu správně implementovat. Složité by to celé asi úplně nebylo, proti poslední API 1.14 se to dá velmi rychle přepsat třeba následovně.

--- dynmap-orig/spigot/src/main/java/org/dynmap/bukkit/DynmapPlugin.java	2019-06-05 00:35:48.000000000 +0200
+++ dynmap/spigot/src/main/java/org/dynmap/bukkit/DynmapPlugin.java	2019-06-05 00:26:32.000000000 +0200
@@ -28,6 +28,8 @@
 import org.bukkit.block.Block;
 import org.bukkit.block.BlockFace;
 import org.bukkit.block.BlockState;
+import org.bukkit.block.data.type.Sign;
+import org.bukkit.block.data.type.WallSign;
 import org.bukkit.command.Command;
 import org.bukkit.command.CommandSender;
 import org.bukkit.entity.Player;
@@ -208,12 +210,29 @@
         public int getBlockIDAt(String wname, int x, int y, int z) {
             World w = getServer().getWorld(wname);
             if((w != null) && w.isChunkLoaded(x >> 4, z >> 4)) {
-                return w.getBlockTypeIdAt(x,  y,  z);
+                return w.getBlockAt(x,y,z).getBlockData().getMaterial().getId();
             }
             return -1;
         }
 
         @Override
+        public boolean isSignAt(String worldName, int x, int y, int z) {
+
+            World world = getServer().getWorld(worldName);
+
+            if ((world != null) && world.isChunkLoaded(x >> 4, z >> 4)) {
+
+                Location loc = new Location(world, x, y, z);
+                Block blk = world.getBlockAt(loc);
+
+                return (blk.getBlockData() instanceof Sign || blk.getBlockData() instanceof WallSign);
+
+            } else {
+                return false;
+            }
+        }
+
+        @Override
         public void scheduleServerTask(Runnable run, long delay) {
             getServer().getScheduler().scheduleSyncDelayedTask(DynmapPlugin.this, run, delay);
         }
@@ -253,9 +272,10 @@
         @Override
         public String getServerName() {
         	try {
-        		return getServer().getServerName();
+        	    return getServer().getName();
         	} catch (NoSuchMethodError x) {	// Missing in 1.14 spigot - no idea why removed...
-        		return getServer().getName();
+        	    return "Spigot/1.14.2-eidacz-dev";
+        		//return getServer().getName();
         	}
         }
         @Override
@@ -799,7 +819,7 @@
 
         /* Get MC version */
         String bukkitver = getServer().getVersion();
-        String mcver = "1.0.0";
+        String mcver = "1.14.2";
         int idx = bukkitver.indexOf("(MC: ");
         if(idx > 0) {
             mcver = bukkitver.substring(idx+5);
@@ -1117,7 +1137,7 @@
                 World w = loc.getWorld();
                 if(!w.isChunkLoaded(loc.getBlockX()>>4, loc.getBlockZ()>>4))
                     continue;
-                int bt = w.getBlockTypeIdAt(loc);
+                int bt = w.getBlockAt(loc).getBlockData().getMaterial().getId();
                 /* Avoid stationary and moving water churn */
                 if(bt == 9) bt = 8;
                 if(btt.typeid == 9) btt.typeid = 8;
@@ -1144,7 +1164,8 @@
     private void checkBlock(Block b, String trigger) {
         BlockToCheck btt = new BlockToCheck();
         btt.loc = b.getLocation();
-        btt.typeid = b.getTypeId();
+        //btt.typeid = b.getTypeId();
+        btt.typeid = b.getBlockData().getMaterial().getId();
         btt.data = b.getData();
         btt.trigger = trigger;
         blocks_to_check_accum.add(btt); /* Add to accumulator */
@@ -1251,9 +1272,9 @@
                     Material m = b.getType();
                     if(m == null) return;
                     switch(m) {
-                        case STATIONARY_WATER:
+                        //case LEGACY_STATIONARY_WATER:
                         case WATER:
-                        case STATIONARY_LAVA:
+                        //case LEGACY_STATIONARY_LAVA:
                         case LAVA:
                         case GRAVEL:
                         case SAND:
@@ -1273,11 +1294,11 @@
                 public void onBlockFromTo(BlockFromToEvent event) {
                     Block b = event.getBlock();
                     Material m = b.getType();
-                    if((m != Material.WOOD_PLATE) && (m != Material.STONE_PLATE) && (m != null)) 
+                    if((m != Material.LEGACY_WOOD_PLATE) && (m != Material.LEGACY_STONE_PLATE) && (m != null))
                         checkBlock(b, "blockfromto");
                     b = event.getToBlock();
                     m = b.getType();
-                    if((m != Material.WOOD_PLATE) && (m != Material.STONE_PLATE) && (m != null)) 
+                    if((m != Material.LEGACY_WOOD_PLATE) && (m != Material.LEGACY_STONE_PLATE) && (m != null))
                         checkBlock(b, "blockfromto");
                 }
             };
DynmapPlugin.java

Hehe… jenže… ono to nejede v principu proti poslednímu API. Jako výchozí je tam totiž napaštěno 1.7, takže není možné používat práci s jednotlivými bloky a jejich nečíselnými daty. Pokud se přepíše v řízení Gradle závislost na moderní API, tak nějak celý projekt shoří a stejně bude nutné použít některé z deprecated metod (získání ID a dat bloku ve formě binárního byte) a navíc, tralalá, se rozbije podpora všeho pod 1.14, takže všechny verze pro Forge a všechny nižší Bukkity. A to je přesně věc, která tady v té horké opravě není chycená za správný konec :). 

Každopádně jako ručně ubastelné něco takového normálně stabilně funguje a díky odstranění většiny starých závislostí je to celé taky o dost menší a hezčí a taky možná svižnější a čipší. Sice to byl jen pokus bez jakékoliv skutečné vize, ale je nutné dodat, že to celé bylo velké dobrodružství — manipulace s Gradle a po drahně dlouhé době, asi tedy po 15 letech na USTAFu, taky lavírování v IntelliJ IDEA, protože to minji doporučil. Velmi velkým přínosem bylo konečně pročtení si téměř celého API Bukkitu, který má rozhodně ohromný potenciál. Ale to asi necháme někomu, kdo by třeba chtěl psát vlastní pluginy. A nám zatím postačí ty tajemné vzkazy na mapě světa.

Tento článek přečetlo již 292 čtenářů (0 dnes).

Komentáře

Nový komentář