Jump to content
Search In
  • More options...
Find results that contain...
Find results in...
Buy OSRS Gold

IceKontroI

[SNIPPET] Event driven local Server/Client framework

Recommended Posts

Overview

A while back I had to create an implementation of a Server and Client communications system for a personal script of mine. My implementation was shit. Here's a much better one. It's still a bit unrefined so if you have an improvement, post it and I'll consider it for revision. The implementation for a lot of the class events is abstract, meaning the user determines what he wants to do when those events fire. Both Server and Client feature a heartbeat system, where after a certain time interval, the Client will send some info to the Server to let it know it's still alive. Likewise every time interval the Server does the same, but for each Client Connection it has saved. Servers and Clients both always have threads open which wait for Objects to be read. When an Object is read, an event will be fired as mentioned below. If the Object read is a Request (covered later on), then it will be displayed through a separate designated event. Implementations of the following events are not mandatory. If you don't want to handle certain events, simply create a constructor for the Server/Client, but leave the blocks for the unused event blank.

Server events

  • public abstract void onConnectionGain(Connection connection);
    • Fired whenever a Client successfully connects to the Server, providing you the Connection that was just established.
  • public abstract void onConnectionLoss(Connection connection, Exception e);
    • Fired whenever a Connection is dropped, except when the Server closes a Connection manually when its shutdown() method is called.
    • The Exception describes the circumstances that lead to the Connection being dropped.
  • public abstract void onWrite(Serializable object);
    • Fired whenever an Object is written to every Connection in the Server's list of current Connections.
  • public abstract void onWrite(Serializable object, Connection connection);
    • Same as above, but fires once for each Connection in the Server's Connection list at the time of writing the Object.
  • public abstract void onRead(Object object, Connection connection);
    • Fires whenever an Object is received on the Server's end from a Client Connection.
    • Does not fire when a Request is received, the following event handles those cases.
  • public abstract void onRequest(Request request, Connection connection);
    • Fires whenever a Request is received on the Server's end from a Client.
    • This class should handle implementation of how exactly you want to handle Requests.
    • Requests will be covered in detail later on in the post.
  • public abstract void onShutdown();
    • Fires at the end of Server#shutdown().

Client events

  • public abstract void onConnectionGain();
    • Fired when the Client successfully established a Connection to the Server.
  • public abstract void onConnectionLoss(Exception e);
    • Fired when the Client's Connection to the Server drops.
    • The Exception describes the circumstances that lead to the Connection being dropped.
  • public abstract void onRead(Object object);
    • Fires when the Client receives an Object from the Server.
    • This again does not fire when a Request is received, that is handled by the event below.
  • public abstract void onRequest(Request request);
    • Fires when a Request is received on the Client's end, from the Server.
  • public abstract void onShutdown();
    • Fires at the end of Client#shutdown().

Functionality

The primary function of a Server/Client implementation like this is to facilitate communication between the Client and Server. Communication can happen across multiple scripts and even multiple computers. They must all be on the same network, however. Reading more than one Object at a time is unsupported (would corrupt underlying streams), and the same for writing. You can, however, read and write at the same time. To set this up, the user must specify which port the Server will be set up on, and then create Clients that attempt to connect to that port. You can do this through the constructors like so:

Spoiler
int port = 2007;

Server server = new Server(port) {
    
    // Unused
    @Override public void onConnectionGain(Connection connection) {}
    @Override public void onConnectionLoss(Connection connection, Exception e) {}
    @Override public void onWrite(Serializable object) {}
    @Override public void onWrite(Serializable object, Connection connection) {}
    @Override public void onRead(Object object, Connection connection) {}
    @Override public void onRequest(Request request, Connection connection) {}
    @Override public void onShutdown() {}
};

Client client = new Client(port) {
    
    // Unused
    @Override public void onConnectionGain() {}
    @Override public void onConnectionLoss(Exception e) {}
    @Override public void onRead(Object object) {}
    @Override public void onRequest(Request request) {}
    @Override public void onShutdown() {}
};

Note  the implementations of each event in the example above do not need to contain any actual code, they just need to have their headers. Clients will automatically attempt to re-connect to the Server with their designated port number. If the Server connection is lost while a Client is still online, it will fire a onConnectionLoss(...) event and wait 1 second before reconnecting. If a Client connection drops while the Server is still online, the Server will simply fire onConnectionLoss(...) and do nothing special.

When a Connection is dropped, either Server or Client, you won't know about it until you try to read/write to it. This is why both Server and Client implement a "heartbeat" system. Every time interval (0.5 seconds) the Server sends a null Object to each Client, and each Client does the same for its Server. This simply ensures a minimum read/write frequency between the Server and Client so that dropped connections can be handled properly. On read/write from a disconnected Connection, an error will be thrown, which will properly remove the Connection from the Server's list and fire onConnectionLoss(...).

Writing

Communication between Server and Client is two-way, meaning the Server can send Objects and Requests to any of its Connections and the Client can do the same to its designated Server. A Server can have as many Connections as your heap space allows, however a Client can only have 1 Server Connection. Reads happen automatically via their own threads, however writes must be handled directly by the user. Anything you want to write must be Serializable, otherwise you'll get an Exception. Here's how the write methods work:

Server

  • public void write(Serializable object)
    • Simply writes the given Serializable to every Connection in the Server's current Connection list.
    • Fires onWrite(Serializable object);
  • public void write(Serializable object, Connection connection)
    • Writes the Serializable to only the specified Connection.
    • Fires onWrite(Serializable object, Connection connection);

Client

  • public void write(Serializable object)
    • Writes the given object
    • Fires onWrite(Serializable object);

When writing Requests, if the Request is unfulfilled (see section below), it will appear in the recipient's onRequest(...) event.

Requests

Finally I'll get to Requests, which is one of the main things I built this system to handle. A Request is a specialized Object, sent to a recipient, with the expectation for it to be returned, but with some modification. A Client may want to send a Request containing a Point with coordinates (-1, -1), expecting it to be returned with different coordinates. Here's an example of what that might look like:

Spoiler
public class CoordinateRequest extends Request<Point> implements Serializable {

    @Override
    public Point execute(Object... args) {
        
        if (args == null || args.length != 2 || !(args[0] instanceof Integer) || !(args[1] instanceof Integer)) {
            throw new IllegalArgumentException("Bad args, expected 2 ints");
        }
        
        return new Point((int) args[0], (int) args[1]);
    }
}

Simply extend Request<T> where T is the type of Object you want to be able to modify and implement Serializable so that the Request can actually be sent. When you initialize the Request<Point>, it will contain a Point (or otherwise specified type) variable called "target" which will be null until the Request is fulfilled. To fulfill the Request, simply call Request#fulfill(Object ... args) with the proper argument parameters (in this case 2 ints). The Request will automatically process the parameters in the way specified by your abstract implementation of Request#execute(Object ... args), and update the "target" from null to whatever the result actually is. If Request#execute(...) throws an Exception at any point, the Request will simply be processed as unfulfilled and ignored, even if it is written back to the sender. Here's what Request fulfillment looks like:

Spoiler
Server server = new Server(2007) {

    @Override 
    public void onRequest(Request request, Connection connection) {
        
        if (request instanceof CoordinateRequest) {
            // Process the Request in a specific way
            request.fulfill(getCoordinateX(), getCoordinateY());
            // Return to sender
            write(request, connection);
        } else {
            // Handle abnormal case
            System.out.println("Unrecognized Request class: " + request.getClass().getSimpleName());
        }
    }
    
    // Unused
    @Override public void onConnectionGain(Connection connection) {}
    @Override public void onConnectionLoss(Connection connection, Exception e) {}
    @Override public void onWrite(Serializable object) {}
    @Override public void onWrite(Serializable object, Connection connection) {}
    @Override public void onRead(Object object, Connection connection) {}
    @Override public void onShutdown() {}
};

To send Requests, simply call the Client or Server's fulfill(...) method. It will write the specified Request to the target(s), wait for it to be returned as fulfilled, and then return it. If it doesn't receive the Request within a designated time frame, it'll throw a RequestTimeoutException. Requests use System.nanoTime() as an identifier to ensure the originally sent Request is returned at the end of the method call. This is a failsafe to ensure you don't accidentally return a different Request to the one that was originally sent out.

Classes

That's it. Here are the classes:

Spoiler
import java.io.Serializable;

public abstract class Request <T extends Serializable> implements Serializable {

    public enum FulfillmentStatus {FULFILLED, UNFULFILLED, FAILED}

    public long nanoTime;
    public FulfillmentStatus status;
    public T target;

    public Request() {

        this(null);
    }

    public Request(T target) {

        this.nanoTime = System.nanoTime();
        this.status = FulfillmentStatus.UNFULFILLED;
        this.target = target;
    }

    public boolean fulfill(Object ... args) {

        try {
            target = execute(args);
            status = FulfillmentStatus.FULFILLED; // Only fulfilled if executed successfully
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            status = FulfillmentStatus.FAILED;
            return false;
        }
    }

    public abstract T execute(Object ... args);
}
Spoiler
import com.IceKontroI.Networking.Requests.CoordinateRequest;
import com.IceKontroI.Networking.Requests.Request;

import java.io.Serializable;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

@SuppressWarnings({"StatementWithEmptyBody", "unchecked", "unused"})
public abstract class Client {

    public int port;
    public volatile Connection connection = null;
    public ConcurrentHashMap<Long, Request> requestQueue = new ConcurrentHashMap<>();
    public Thread readInput = new Thread(() -> {
        while (!Thread.interrupted()) {
            Object object;
            do {
                while (connection == null) {}
                try {
                    object = connection.read();
                    if (object != null) { // Usually heartbeat if null
                        break;
                    }
                } catch (Exception e) {connection = null;}
            } while (true);
            try {
                if (object instanceof Request) {
                    Request request = (Request) object;
                    switch (request.status) {
                        case FULFILLED: {
                            requestQueue.put(request.nanoTime, request);
                            break;
                        }
                        case UNFULFILLED: {
                            onRequest(request);
                            break;
                        }
                        default: break; // Ignore failed requests
                    }
                } else {
                    onRead(object);
                }
            } catch (Exception e) {e.printStackTrace();} // Prevent user error from killing thread
        }
    });
    public Thread reconnect = new Thread(() -> {
        Exception exception = null;
        while (!Thread.interrupted()) {
            while (connection == null) {
                if (exception != null) {
                    onConnectionLoss(exception);
                    try {
                        Thread.sleep(TimeUnit.SECONDS.toMillis(1));
                    } catch (Exception ignored) {}
                }
                try {
                    Socket socket = new Socket();
                    socket.connect(new InetSocketAddress(port));
                    connection = Connection.connectClient(socket);
                    onConnectionGain();
                } catch (Exception e) {
                    connection = null;
                    exception = e;
                }
            }
        }
    });
    public Thread heartbeat = new Thread(() -> {
        while (!Thread.interrupted()) {
            try {
                Thread.sleep(TimeUnit.MILLISECONDS.toMillis(500));
            } catch (InterruptedException e) {e.printStackTrace();}
            write(null);
        }
    });

    public Client(int port) {

        this.port = port;
        this.readInput.start();
        this.reconnect.start();
        this.heartbeat.start();
    }

    public void write(Serializable object) {

        while (connection == null) {}
        try {
            connection.write(object);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public abstract void onConnectionGain();

    public abstract void onConnectionLoss(Exception e);

    public abstract void onRead(Object object);

    public abstract void onRequest(Request request);

    public abstract void onShutdown();

    public <T extends Request> T fulfill(T request) throws RequestTimeoutException {

        write(request);
        long start = System.nanoTime();
        requestQueue.keySet().forEach(nanoTime -> {
            if (start - nanoTime > TimeUnit.SECONDS.toNanos(100)) {
                requestQueue.remove(nanoTime);
            }
        });
        do {} while (System.nanoTime() - start < TimeUnit.MILLISECONDS.toNanos(500) && !requestQueue.keySet().contains(request.nanoTime));
        if (System.nanoTime() - start > TimeUnit.MILLISECONDS.toNanos(500)) {
            throw new RequestTimeoutException(request.getClass().getSimpleName() + " was not fulfilled by Server within the timeout period");
        }
        return (T) requestQueue.get(request.nanoTime); // Fulfilled Request always matches type T, but Compiler disagrees
    }

    public void shutdown() {

        readInput.interrupt();
        reconnect.interrupt();
        heartbeat.interrupt();
        connection.close();
    }
}
Spoiler
import com.IceKontroI.Networking.Requests.Request;
import javafx.util.Pair;

import java.io.Serializable;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.TimeUnit;

@SuppressWarnings({"StatementWithEmptyBody", "unchecked", "unused"})
public abstract class Server {

    public int port;
    public ServerSocket serverSocket;
    public ConcurrentHashMap<Connection, Thread> connections = new ConcurrentHashMap<>();
    public ConcurrentHashMap<Long, Pair<Request, Connection>> requestQueue = new ConcurrentHashMap<>();
    public Thread heartbeat = new Thread(() -> {
        while (!Thread.interrupted()) {
            try {
                Thread.sleep(TimeUnit.MILLISECONDS.toMillis(500));
            } catch (InterruptedException e) {e.printStackTrace();}
            write(null);
        }
    });

    public Server(int port) {

        this.port = port;
        try {
            this.serverSocket = new ServerSocket(port);
        } catch (Exception e) {
            e.printStackTrace();
            System.exit(port);
        }
        heartbeat.start();
        connect(); // Start the connection chain
    }

    public void connect() {

        new Thread(() -> {
            try {
                Socket socket = serverSocket.accept(); // Wait until a connection is made
                if (socket != null) {
                    Connection connection = Connection.connectServer(socket);
                    if (connection != null) {
                        onConnectionGain(connection);
                        Thread reader = new Thread(() -> {
                            while (!Thread.interrupted()) {
                                try {
                                    Object object = connection.read();
                                    if (object != null) { // Usually heartbeat if null
                                        try {
                                            if (object instanceof Request) {
                                                Request request = (Request) object;
                                                switch (request.status) {
                                                    case FULFILLED: {
                                                        requestQueue.put(request.nanoTime, new Pair<>(request, connection));
                                                        break;
                                                    }
                                                    case UNFULFILLED: {
                                                        onRequest(request, connection);
                                                        break;
                                                    }
                                                    default:
                                                        break; // Ignore failed requests
                                                }
                                            } else {
                                                onRead(object, connection);
                                            }
                                        } catch (Exception e) {e.printStackTrace();} // Prevent user error from killing thread
                                    }
                                } catch (Exception e) {disconnect(connection, e);}
                            }
                        });
                        reader.start();
                        connections.put(connection, reader);
                    }
                }
            } catch (Exception e) {e.printStackTrace();}
            connect(); // Extend the connection chain
        }).start();
    }

    public void disconnect(Connection connection, Exception e) {

        if (!(e instanceof ServerShutdownException)) {
            onConnectionLoss(connection, e);
        }
        connection.close();
        if (connections.containsKey(connection)) {
            connections.get(connection).interrupt();
            connections.remove(connection);
        }
    }

    public void write(Serializable object) {

        onWrite(object);
        connections.keySet().forEach(connection -> {
            onWrite(object, connection);
            write(object, connection);
        });
    }

    public void write(Serializable object, Connection connection) {

        try {
            connection.write(object);
        } catch (Exception e) {disconnect(connection, e);}
    }

    public abstract void onConnectionGain(Connection connection);

    public abstract void onConnectionLoss(Connection connection, Exception e);

    public abstract void onWrite(Serializable object);

    public abstract void onWrite(Serializable object, Connection connection);

    public abstract void onRead(Object object, Connection connection);

    public abstract void onRequest(Request request, Connection connection);

    public abstract void onShutdown();

    public <T extends Request> CopyOnWriteArrayList<Pair<T, Connection>> fulfill(T request) {

        CopyOnWriteArrayList<Pair<T, Connection>> fulfilled = new CopyOnWriteArrayList<>();
        long start = System.nanoTime();
        requestQueue.keySet().forEach(nanoTime -> {
            if (start - nanoTime > TimeUnit.SECONDS.toNanos(100)) {
                requestQueue.remove(nanoTime);
            }
        });
        connections.keySet().stream().parallel().forEach(connection -> {
            write(request, connection);
            do {} while (System.nanoTime() - start < TimeUnit.MILLISECONDS.toNanos(500) && !requestQueue.keySet().contains(request.nanoTime));
            if (System.nanoTime() - start > TimeUnit.MILLISECONDS.toNanos(500)) {
                return;
            }
            fulfilled.add(new Pair<>((T) requestQueue.get(request.nanoTime).getKey(), connection)); // Fulfilled Request always matches type T, but Compiler disagrees
        });
        return fulfilled;
    }

    public void shutdown() {

        heartbeat.interrupt();
        connections.keySet().forEach(connection -> disconnect(connection, new ServerShutdownException("Closing connection " + connection.socket.getLocalAddress() + " on Server shutdown")));
        onShutdown();
    }
}
Spoiler
public class ServerShutdownException extends Exception {

    public ServerShutdownException(String message) {

        super(message);
    }
}
Spoiler
public class RequestTimeoutException extends Exception {

    public RequestTimeoutException(String message) {

        super(message);
    }
}
Spoiler
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.net.Socket;

public class Connection {

    // Vars are final so they can be used as reliable locks/monitors for synchronization

    public final Socket socket;
    public final ObjectInputStream input;
    public final ObjectOutputStream output;

    // Constructor

    public static Connection connectServer(Socket socket) {

        try { // Server and client I/O stream order creation order reversed to prevent deadlock
            ObjectInputStream input = new ObjectInputStream(socket.getInputStream());
            ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());
            return new Connection(socket, input, output);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    public static Connection connectClient(Socket socket) {

        try { // Server and client I/O stream order creation order reversed to prevent deadlock
            ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());
            ObjectInputStream input = new ObjectInputStream(socket.getInputStream());
            return new Connection(socket, input, output);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    public Connection(Socket socket, ObjectInputStream input, ObjectOutputStream output) {

        this.socket = socket;
        this.input = input;
        this.output = output;
    }

    // Synchronized I/O

    public void write(Serializable object) throws Exception {

        // Synchronized independently of read(), just need to prevent concurrent writes on same stream
        synchronized (output) {
            output.writeObject(object);
            output.reset(); // ObjectOutputStreams cause memory leaks since they keep a reference to everything they write
        }
    }

    public Object read() throws Exception {

        // Synchronized independently of write(), just need to prevent concurrent reads on same stream
        synchronized (input) {
            return input.readObject();
        }
    }

    public void close() {

        // Heavily synchronized, ignore errors
        try {
            synchronized (socket) {
                socket.close();
            }
        } catch (Exception ignored) {}
        try {
            synchronized (input) {
                input.close();
            }
        } catch (Exception ignored) {}
        try {
            synchronized (output) {
                output.close();
            }
        } catch (Exception ignored) {}
    }
}

 

Edited by IceKontroI
  • Like 1
  • Thanks 1

Share this post


Link to post
Share on other sites

Interesting post, im sure you will help out some people with this.

I would however recommend to to release the source on a github page instead of pasting it inside your thread.

Also, in your connect function you're using multiple nested try{}catch{} statements, where your simply catch Exception. Since your entire function body is already wrapped inside a try{}catch{Exception e}, it is redundant to also wrap each individual element in one aswell (it will trigger when an exception is thrown anyways), since you're catching the same exception. If you were to catch the specific exception that method can throw, it would not be redundant.

I would also like to see the abstract methods in your code before your methods with a body, so you can immediately see which methods you still have to implement yourself.

 

 

Share this post


Link to post
Share on other sites
44 minutes ago, gef30 said:

Also, in your connect function you're using multiple nested try{}catch{} statements, where your simply catch Exception. Since your entire function body is already wrapped inside a try{}catch{Exception e}, it is redundant to also wrap each individual element in one aswell (it will trigger when an exception is thrown anyways), since you're catching the same exception. If you were to catch the specific exception that method can throw, it would not be redundant.

I went back and looked at it and the structure isn't actually redundant. Each try/catch does serve a purpose. I've added comments to show the purpose of each, but essentially the first one is obligatory and doesn't do anything, the second actually disconnects the Connection because of a read failure, and the third prevents the user's implementation of events from shutting down the thread.

            try { // Initial try/catch to deal with error thrown by serverSocket.accept()
                Socket socket = serverSocket.accept();
                if (socket != null) {
                    Connection connection = Connection.connectServer(socket);
                    if (connection != null) {
                        onConnectionGain(connection);
                        Thread reader = new Thread(() -> {
                            while (!Thread.interrupted()) { // This gets interrupted at the end of method call disconnect(connection, exception)
                                try { // This one catches specifically read() errors, and forces a Connection disconnect
                                    Object object = connection.read();
                                    if (object != null) {
                                        try { // This one prevents errors caused by user's implementation of onRequest() and onRead(), but doesn't force a disconnect
                                            if (object instanceof Request) {
                                                Request request = (Request) object;
                                                switch (request.status) {
                                                    case FULFILLED: {
                                                        requestQueue.put(request.nanoTime, new Pair<>(request, connection));
                                                        break;
                                                    }
                                                    case UNFULFILLED: {
                                                        onRequest(request, connection);
                                                        break;
                                                    }
                                                    default:
                                                        break; // Ignore failed requests
                                                }
                                            } else {
                                                onRead(object, connection);
                                            }
                                        } catch (Exception e) {e.printStackTrace();} // Prevent user error from killing thread
                                    }
                                } catch (Exception e) {disconnect(connection, e);}
                            }
                        });
                        reader.start();
                        connections.put(connection, reader);
                    }
                }
            } catch (Exception e) {e.printStackTrace();}
            connect(); // Extend the connection chain

Share this post


Link to post
Share on other sites
11 minutes ago, IceKontroI said:

I went back and looked at it and the structure isn't actually redundant. Each try/catch does serve a purpose. I've added comments to show the purpose of each, but essentially the first one is obligatory and doesn't do anything, the second actually disconnects the Connection because of a read failure, and the third prevents the user's implementation of events from shutting down the thread.

            try { // Initial try/catch to deal with error thrown by serverSocket.accept()
                Socket socket = serverSocket.accept();
                if (socket != null) {
                    Connection connection = Connection.connectServer(socket);
                    if (connection != null) {
                        onConnectionGain(connection);
                        Thread reader = new Thread(() -> {
                            while (!Thread.interrupted()) { // This gets interrupted at the end of method call disconnect(connection, exception)
                                try { // This one catches specifically read() errors, and forces a Connection disconnect
                                    Object object = connection.read();
                                    if (object != null) {
                                        try { // This one prevents errors caused by user's implementation of onRequest() and onRead(), but doesn't force a disconnect
                                            if (object instanceof Request) {
                                                Request request = (Request) object;
                                                switch (request.status) {
                                                    case FULFILLED: {
                                                        requestQueue.put(request.nanoTime, new Pair<>(request, connection));
                                                        break;
                                                    }
                                                    case UNFULFILLED: {
                                                        onRequest(request, connection);
                                                        break;
                                                    }
                                                    default:
                                                        break; // Ignore failed requests
                                                }
                                            } else {
                                                onRead(object, connection);
                                            }
                                        } catch (Exception e) {e.printStackTrace();} // Prevent user error from killing thread
                                    }
                                } catch (Exception e) {disconnect(connection, e);}
                            }
                        });
                        reader.start();
                        connections.put(connection, reader);
                    }
                }
            } catch (Exception e) {e.printStackTrace();}
            connect(); // Extend the connection chain

though simply catching Exception multiple times is bad practise, i would definitely change the Exception to the actual exception which is thrown.

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


  • Similar Content

    • By Fluffee
      Fluffee's Server Setup 3.0
      As some of you may know, I released an automated server setup script a little over a year ago. Albeit that script worked well, I was never fully satisfied with it, as I found it to be kind of messy to use. As a result, I rewrote the script, and restructured it, to add more, and stronger support for the different versions of Debian, the different versions of CentOS and the different versions of Ubuntu. However, with so many different Operating Systems being supported, and many different server providers having different setups, it's hard for me to test every possible scenario on my own. Which is why I've been privately handing this script out, and am now publicly looking for help.

      What does the script do?
      Similar to my previous setup script, this script changes the SSH port and creates a new user for connecting and using the server and disables root SSH connections; it installs the latest version of TightVNC Server (1.3.10) and sets that up with the desired port and password. It installs JDK 102 (32 or 64 bit depending on the OS), and installs TRiBot and OSBuddy and sets up the appropriate .jar file associations.
      What operating systems does it support?
      - CentOS (6.x, 7.x, 8.x) (32 and 64 bit)
      - Debian (7.x, 8.x, 9.x, 10.x) (32 and 64 bit)
      - Ubuntu (12.x, 14.x.16.x, 18.x, 20.x) (32 and 64 bit)
      Does it work?
      As far as I know, yes it does in fact work, and it works well if I might add. However, given that I can't test every possible setup, there is the potential for differences and issues
      How do I run the script?
      Login as root to a fresh VPS/dedi running one of the supported operating systems. From there run the commands listed below and follow the onscreen instructions. For those who would like to see the script's code, it can be found on my bitbucket here. The commands to run are as follows:
      (curl -kO https://bitbucket.org/teamfluffee/fluffees-server-setup/raw/master/fluffees-server-setup.sh || wget --no-check-cert https://bitbucket.org/teamfluffee/fluffees-server-setup/raw/master/fluffees-server-setup.sh) && chmod +x fluffees-server-setup.sh && bash fluffees-server-setup.sh -v Version History
       
    • By Rileyz
      Hello Tribot community!

      The team has been working hard at creating better value propositions for the community with the new repository on its way.
      We have two questions that will help us better serve you:
      1. Would you like to see scripts being bundled and sold together at a discount?
      2. Which scripts do you think compliment each other best?
      Cheers!
      - RileyZ
    • By CrudeCorrosionWatcher
      I'm having difficulties connecting my bots through my proxies.  I've confirmed that the proxies themselves are working by connecting directly through them in my LAN settings on windows, and then visiting WhatsMyIP.  Using the LAN settings method I'm also able to load TRiBot and load a client.  When I try loading Tribot normally without proxies, it loads,and launching clients without proxies works.  If I try launching tribot OR a client with the same proxies I had confirmed working, I get a timeout in the former case and a perpetual "Loading client params" in the latter.
      Has anybody else experienced anything similar and is there something obvious I'm overlooking? Thank you for your input
      tribot_error.txt
    • By Realist
      Need Tribot credits for scripts/VIP etc? You're at the right place!
      or
      Do you have credits you want to SELL? Message me!
      I accept cash & 07 Gold/ Rs3 Gold / CS:GO Skins.
       
      1-5 credits is 3m each, other amounts = 1.8-2.3m per credit
       
      Skype is live:realistgold
       
      Discord: Realist#1834 
      Unique Discord ID: 194091681836957696
    • By MNO
      We offer 3 packages, all packages come with the following things pre-installed:
      Windows 10 Java Google Chrome Firefox Dreambot You will get acces through a Remote Desktop Connection (rdp) which we will setup for you.
      Ordering can be done through discord:
      Server: https://discord.gg/jzwNzED
      User: Nex#8648 (UID: 109992979057373184)
      We accept most crypto & rsgp. (Paypal for trusted users)
      We currently offer the following packages:
      Bronze Server
      CPU: Intel Core i7-4770  (or equiv) RAM: 8 GB  Can run: ~12 bots $40/Month
      Iron Server
      CPU: Intel Xeon E3-1275V6  (or equiv) RAM: 64 GB  Can run: ~17 bots $60/Month
      Steel Server
      CPU: AMD Ryzen 7 1700X (or equiv) RAM: 64 GB  Can run: ~25 bots $80/Month  
      TOS:
      1) You will not use this service for any illegal activity.
      2) You will notify us in time for a renewal of service's (>1 week ahead).
      3) All payments are final and we offer no refunds.
      4) By using our service you agree to these terms.
      Breaking any of these will result in instant termination of service with NO refund.
       
    • By Friday321
      Hey I’m new to hitting on mobile and need help
    • By schenkelenkel
      Getting this error, client won't start.

    • By Rileyz
      TRiBot is looking to improve a lot of its customer relationship management, customer on boarding process, customer experience, design elements, community engagement and pretty much everything else you can imagine when it comes to marketing.
      Our goal: To ensure that the marketing done TRULY reflects the experience and does not shine an inaccurate light on what TRiBot is lacking in.
      So I ask, what do you love about TRiBot and what do you hate about TRiBot? What does O S Bot, Rune M8, PowR Bot and Dre amBot do better? (yes I purposely didn't spell it right 😂).
      Love, 
      RileyZ
    • Guest
      By Guest
      looking for private dice bot. i'd explain but it's straight forward just the basic spam messages and bot. looking to be low ban rate lmk how much u guys charge for ur scrips !
    • By TheIronPride
      Hey everyone i been using looking glass for a few months now, and just today i no longer see it as an option, i only see "new Client" and "New Client (advanced)" where there is normally a "new Client (looking Glass)" option, any suggestions?
  • Our picks

    • TRiBot 12 Release Candidate

      The TRiBot team has been hard at work creating the last major version of TRiBot before the TRiBot X release. We've noticed many problems with TRiBot 11 with a lot of users preferring TRiBot 10 over 11. We've heard you, so we took TRiBot 10, added the new features introduced with 11, introduced some other new things, and created TRiBot 12. So without further adieu, here's TRiBot 12.
        • Sad
        • Like
      • 18 replies
    • Gradle is a build tool used to accelerate developer productivity.

      We recently setup a Maven repository (TRiBot Central) to make it easier for scripters to create scripts. Check it out here: https://gitlab.com/trilez-software/tribot/tribot-central/-/packages

      Furthermore, we've released a simple Gradle project to make it easy to run TRiBot and develop scripts for it. Check it out here: https://gitlab.com/trilez-software/tribot/tribot-gradle-launcher

      The goals of TRiBot Central are to:

      Deliver updates to TRiBot faster


      Better organize TRiBot's dependencies (AKA dependancies)


      Make it easier to develop scripts for TRiBot


      Make it easier to use and run TRiBot


      Note: TRiBot won't be able to run scripts from within this project until TRiBot's next release.
        • Like
      • 10 replies
    • Hi everyone,

      I'd like to thank everyone for their patience in this transition period. Since last week, we've worked out the remaining bugs with this integration.

      Some users have still been having issues with connecting their forums account to their Auth0 account. To resolve this, we've imported all forums accounts into Auth0.

      Unfortunately, the accounts which were imported today were using an unsupported password hashing algorithm. Hence, random passwords were set during the import.

      What does this mean for me?

      If you've previously linked your forums account to your Auth0 account, you don't have to do anything. Nothing changes for you.


      If you haven't logged in via our new login yet,

      Try logging in with your forums email address and the last password you used


      If you are unable to login, please use the "Forgot password" tool on the login page:
      Follow the instructions to reset your password
       
        • thonking
        • Like
      • 17 replies
    • Hello everyone,

      Last week we tried to roll out Auth0 Login, but we lost that battle. Now it's time to win the war!

      Important changes

      When logging into the client, you'll now have to enter your Auth0 account credentials instead of your forums credentials

      Note: 2FA is still handled through your forums account (for the time being)



      Changes for existing users

      You'll have to link your Auth0 account to your forums account here: https://tribot.org/forums/settings/login/?service=11


      Auth0 accounts have been created for most existing users. Please use your forums email address and password to login.



      Important notes

      Make sure to verify your email address upon creating a new Auth0 account


      When we mention your Auth0 account, we mean your account used for auth.tribot.org as displayed below
      • 81 replies
    • To better support the upcoming changes (TRiBot X, new repository), we're switching our login handler to Auth0. Instead of logging in with the standard form, you'll now be required to login through our Auth0 application.

      All existing accounts which have been used within approximately the past year have been imported into Auth0 using the same email and password combination which has been stored on the forums.

      What does this mean for users?

      Your account credentials are now even more securely stored


      You'll be able to login via Facebook, Google, and others in the future


      Is there anything users have to do differently now?

      Existing users: You'll have to login with the standard login, open your Account Settings, then link your Auth0 account


      New users: You'll be redirected to our Auth0 app (auth.tribot.org) where you'll be able to create an account


      Why was this change made?

      The new apps we are creating (such as the new repository) aren't able to use the forums to handle user logins


      To centralize all user accounts in one area


      To ensure that the client login doesn't go down when the forums are having problems


      To speed up our development


      Other considerations

      There's no documentation or official support for using Invision Community combined with Auth0, so there are still a few kinks we're working out


      We're in the works of creating an account management panel specifically for Auth0 accounts (ETA August)


      It's not possible to change email addresses for the time being (this will be resolved this August)


      Changing passwords is a weird process for the time being. To change your password, you'll have to use the "Don't remember your password" tool on the Auth0 login page
        • Like
      • 11 replies
  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...