Jump to content
Sign in to follow this  
Wrecker

favorite bit of code you wrote?

Recommended Posts

what's your favorite implementation that you have come up with to solve a problem?

my favorite to date is on a project that I pretty much gave up on when I realized how much work was ahead of me when I wanted to rewrite it into a proper implementation. I was just writing it for an end result and when I realized how fucked I was going to be if I ever released it and expected to be able to update/modify it without breaking a stupid amount of dependencies, I pretty much left it where it was. it was supposed to be a MC Spigot RPG plugin where you created a wolf pack, fought to steal resources and shit from other packs, leveled up your characters, had other npc wolves that were characters and a part of packs that claimed their own territories and stuff like that who you could wage war against/try to coerce into joining your own pack alongside other players, but it worked into a giant mess that would work initially, but would be susceptible to a shit-ton of broken dependencies for some simple additions/changes, and I was embarrassed so I left it where it was.

One thing I was proud of, though, was using A* search to figure out paths to travel, the idea was to be able to send AI wolves from your pack to go out and scout other packs and report their findings when they returned... The next step was to build a few separate databases of blocks and hold them in a queue, so individual threads could access them and figure out where they were going without fucking over the main thread... Maybe even build some better heuristics, but fuck I was happy when everything worked as it was the first time...

Node:

 class Node implements Comparable<Node> {
     private Node parentNode = null;
     ArrayList<Node> neighbours = new ArrayList<>();

     private boolean isClosed = false;

     private int f;
     private int g = Integer.MAX_VALUE;
     private int h;

     private int x;
     private int y;
     private int z;

    private RouteFinder finder;

     Node(int x, int y, int z, RouteFinder finder) {
        this.x = x;
        this.y = y;
        this.z = z;

        this.finder = finder;
        finder.nodes.add(this);
        setHeuristic();
    }

    private boolean nodeExistsAt(int x, int y, int z) {
        for (Node node : finder.nodes) if (node.x == x && node.y == y && node.z == z) return true;
        return false;
    }

    private boolean isObstacle(int x, int y, int z) {
        World world = finder.world;

        return world.getBlockAt(x,y,z).getType().isSolid() ||
                !world.getBlockAt(x,y,z).getType().isSolid() &&
                        !world.getBlockAt(x,y-1,z).getType().isSolid();
    }

    private void setHeuristic() {
        h = Math.abs(x - finder.toX) + Math.abs(y - finder.toY) + Math.abs(z - finder.toZ);
    }

    void setF() {
        f = g + h;
    }

    int getG() {
        return g;
    }

    int checkG(Node node) {
         if (node.y > y || node.y < y) return 2;
         else return 1;
    }

    void setG(int g) {
        this.g = g;
    }

    boolean isComplete() {
        return x == finder.toX && y == finder.toY && z == finder.toZ;
    }

    void setParentNode(Node node) {
        this.parentNode = node;
    }

     Node getParentNode() {
         return parentNode;
     }

     int getX() {
         return x;
     }

     int getY() {
         return y;
     }

     int getZ() {
         return z;
     }

     void setNeighbours() {
        for (int i = -1; i < 2; i++) {
            for (int j = -1; j < 2; j++) {
                for (int k = -1; k < 2; k++) {
                    int cX = x + i;
                    int cY = y + k;
                    int cZ = z + j;

                    if (!nodeExistsAt(cX,cY,cZ) && !isObstacle(cX,cY,cZ)) {
                        Node node = new Node(cX,cY,cZ,finder);
                        neighbours.add(node);
                    }
                }
            }
        }
    }

    boolean isClosed() {
         return isClosed;
    }

    void setClosed() {
         isClosed = true;
    }

     @Override
    public int compareTo(@Nonnull Node node) {
         return Integer.compare(f, node.f);
    }
}

RouteFinder:

public class RouteFinder implements RouteObserver {
    private static final long TRAVEL_TIMEOUT = 10000;
    private static final long RETRY_TIMEOUT = 500;
    private static final long TELEPORT_TIMEOUT = 8000;
    private static final int BLOCK_ACCURACY = 2;

    ArrayList<Node> nodes = new ArrayList<>();
    private ArrayList<Node> openSet = new ArrayList<>();

    private ArrayList<Location> route = new ArrayList<>();

    private boolean isEntityLoaded;

    private EntityInsentient entity;

    private RouteSubject subject;

    private int fromX;
    private int fromY;
    private int fromZ;

    int toX;
    int toY;
    int toZ;

    World world;

    public RouteFinder(EntityInsentient entity, World world) {
        this.entity = entity;
        isEntityLoaded = world.getChunkAt((int)entity.locX,(int)entity.locZ).isLoaded();
        this.subject = WolfPack.getChunkLoadListener();
        this.world = world;
    }

    private void setRoute() {
        long startTime = System.currentTimeMillis();
        long endTime;
        long totalTime;
        Node node = new Node(fromX,fromY,fromZ,this);
        node.setG(0);
        openSet.add(node);

        while (!openSet.isEmpty()) {

            Collections.sort(openSet);
            Node current = openSet.get(0);
            current.setNeighbours();

            openSet.remove(current);
            current.setClosed();

            if (current.isComplete()) {

                //DEBUG
                endTime = System.currentTimeMillis();
                totalTime = endTime - startTime;
                Debugger.debug("Execution took: " + totalTime + "ms");
                //DEBUG

                //Route found
                buildRoute(current);
                return;
            }
            for (Node neighbour : current.neighbours) {
                if (neighbour.isClosed()) continue;
                if (!openSet.contains(neighbour)) openSet.add(neighbour);
                int tempG = current.getG() + neighbour.checkG(current);
                if (tempG >= neighbour.getG()) continue;
                neighbour.setParentNode(current);
                neighbour.setG(tempG);
                neighbour.setF();
            }
        }
        //No route found
    }

    private void buildRoute(Node node) {
        Node current = node;
        while (current.getParentNode() != null) {
            route.add(new Location(world,current.getX(),current.getY(),current.getZ()));
            current = current.getParentNode();
        }
        nodes.clear();
        openSet.clear();
        Collections.reverse(route);
    }

    public void moveEntity(Location to) {
        subject.addObserver(this);

        this.fromX = (int) entity.locX;
        this.fromY = (int) entity.locY;
        this.fromZ = (int) entity.locZ;

        this.toX = to.getBlockX();
        this.toY = to.getBlockY();
        this.toZ = to.getBlockZ();

        setRoute();

        if (isEntityLoaded) moveEntityLoaded();
        else moveEntityUnloaded();
    }

    private void moveEntityLoaded() {
        new BukkitRunnable() {
            public void run() {

                //DEBUG
                int debugRetryAttempt = 0;
                //DEBUG

                Iterator<Location> iterator = route.iterator();

                while (iterator.hasNext()) {
                    Location go = iterator.next();

                    long timeout = System.currentTimeMillis() + TRAVEL_TIMEOUT;
                    long retryTravel = System.currentTimeMillis() + RETRY_TIMEOUT;
                    long teleportTravel = System.currentTimeMillis() + TELEPORT_TIMEOUT;

                    int entityX;
                    int entityY;
                    int entityZ;

                    int goToX = go.getBlockX();
                    int goToY = go.getBlockY();
                    int goToZ = go.getBlockZ();

                    boolean arrived = false;

                    entity.getNavigation().a(goToX, goToY, goToZ, 1D);

                    while (!arrived) {
                        if (!isEntityLoaded) {
                            moveEntityUnloaded();
                            cancel();
                        }

                        entityX = (int) entity.locX;
                        entityY = (int) entity.locY;
                        entityZ = (int) entity.locZ;

                        //Check to see if the entity has arrived at its destination
                        if (entityX >= goToX - BLOCK_ACCURACY && entityX <= goToX + BLOCK_ACCURACY
                                && entityY >= goToY - BLOCK_ACCURACY && entityY <= goToY + BLOCK_ACCURACY
                                && entityZ >= goToZ - BLOCK_ACCURACY && entityZ <= goToZ + BLOCK_ACCURACY) {
                            arrived = true;
                            iterator.remove();
                        }

                        //Probably won't occur with the teleport aspect added in, but just for safety's sake
                        if (System.currentTimeMillis() >= timeout) {

                            //DEBUG
                            Debugger.debug("Cancelling route.");
                            //DEBUG

                            cancel();
                            return;
                        }

                        //Retry the next step of the route, they get distracted a lot
                        if (System.currentTimeMillis() >= retryTravel) {
                            entity.getNavigation().a(goToX, goToY, goToZ, 1D);
                            retryTravel = System.currentTimeMillis() + RETRY_TIMEOUT;
                            debugRetryAttempt++;
                        }

                        //Teleport the entity if it is stuck
                        if (System.currentTimeMillis() >= teleportTravel) {
                            //DEBUG
                            Debugger.debug("Teleporting...");
                            //DEBUG

                            entity.setPosition(go.getX(),go.getY(),go.getZ());

                            teleportTravel = System.currentTimeMillis() + TELEPORT_TIMEOUT;
                        }

                        try {
                            Thread.sleep(10);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
                //DEBUG
                Debugger.debug("Route COMPLETE! Retries: " + debugRetryAttempt);
                //DEBUG

                cancel();
                //Route complete
            }
        }.runTaskAsynchronously(WolfPack.getPlugin());
        subject.removeObserver(this);
    }

    private void moveEntityUnloaded() {
        //Speed Formula
        //y = 43.178x-0.02141
        double blocksPerMillisecond = (43.178 *
                entity.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED).getValue() - 0.02141)/1000;

        new BukkitRunnable() {
            public void run() {

                Iterator<Location> iterator = route.iterator();
                double blockTravelled = 10/blocksPerMillisecond;
                int counter = 0;

                Location go = iterator.next();

                //Emulate the entity travelling the route through the unloaded chunk
                while (iterator.hasNext()) {

                    while (counter <= blockTravelled) {
                        entity.setPosition(go.getX(),go.getY(),go.getZ());

                        if (isEntityLoaded) {
                            moveEntityLoaded();
                            cancel();
                        }

                        counter = counter + 10;
                        iterator.remove();

                        iterator.next();

                        try {
                            Thread.sleep(10);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
                cancel();
            }
        }.runTaskAsynchronously(WolfPack.getPlugin());
        subject.removeObserver(this);
    }

    @Override
    public void update(boolean loaded) {
        this.isEntityLoaded = loaded;
    }

    @Override
    public EntityInsentient getEntity() {
        return entity;
    }
}

Tried to implement the Observer pattern to check if an Entity happened to be unloaded from the server so it could continue its route:

public interface RouteObserver {
    void update(boolean loaded);

    EntityInsentient getEntity();
}
public interface RouteSubject {
    void addObserver(RouteObserver observer);

    void removeObserver(RouteObserver observer);

    void notifyObservers(ArrayList<EntityInsentient> entities, boolean loaded);
}
public class ChunkLoadListener implements Listener, RouteSubject {
    private ArrayList<RouteObserver> observers = new ArrayList<>();

    @EventHandler
    public void onChunkUnload(ChunkUnloadEvent event) {
        notifyObservers(buildEntityList(event.getChunk()),false);
    }

    @EventHandler
    public void onChunkLoad(ChunkLoadEvent event) {
        notifyObservers(buildEntityList(event.getChunk()),true);
    }

    @Override
    public void addObserver(RouteObserver observer) {
        observers.add(observer);
    }

    @Override
    public void removeObserver(RouteObserver observer) {
        observers.remove(observer);
    }

    private ArrayList<EntityInsentient> buildEntityList(Chunk chunk) {
        ArrayList<EntityInsentient> entities = new ArrayList<>();
        for (Entity entity : chunk.getEntities()) {
            if (((CraftEntity) entity).getHandle() instanceof  EntityInsentient) {
                entities.add((EntityInsentient) ((CraftEntity) entity).getHandle());
            }
        }
        return entities;
    }

    @Override
    public void notifyObservers(ArrayList<EntityInsentient> entities, boolean loaded) {
        for (RouteObserver observer : observers) {
            for (EntityInsentient entity : entities) {
                if (observer.getEntity() == entity) observer.update(loaded);
            }
        }
    }
}

 

That was a while ago, and I have no idea what observers is doing in ChunkLoadListener after reading it in this post, but I do remember it being a problem with it waiting for it to say that there's an Entity that should be emulated to move instead of actually moved... Fuck me, it might not be moving at all after unload maybe with nothing looking to see where it's at? Been a while since I've dealt with Spigot or looked at this code lol.

Erm, fuck it, what have you written that does exactly what you want it to or at least close enough that you're happy with where you're at in you goals?

Share this post


Link to post
Share on other sites

It's hard to have a 'favourite' piece of code, really, but the one I've used, updated and adapted most often is this one. It's nothing big, fancy and glamorous, it just provides a simple way to generate a random integer within a given range in a single line of code. Language is Swift 4.

public func randomNumber<T : SignedInteger>(inRange range: ClosedRange<T> = 1...6) -> T { 
	let length = Int64(range.upperBound - range.lowerBound + 1) 
	let value = Int64(arc4random()) % length + Int64(range.lowerBound) 
	return T(value) 
}

- F

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

  • Recently Browsing   0 members

    No registered users viewing this page.

×