This repository consists of several Java libraries. Most of which are intended for use by other Java software. They are available in the Maven Central Repositories which allows for easily importing the library into your build automation software of choice.
Tip: GitHub has a handy Table of Contents feature available by clicking the list icon on the top left of this document.
The libraries maintained in this repository utilize the SLF4J logging framework. It provides no logging implementation by default, meaning that logs will be silently ignored. To enable logging, you must add a logging implementation to your class path which is typically done by adding the implementation to your project's dependencies in Maven or Gradle. Popular implementations include Logback, log4j2, slf4j-simple, and SLF4J's adapter for the built-in java.util.logging framework.
The Receiver library is the main library of the project. It allows your video game (or modification) to receive effect requests from a streamer using the Crowd Control desktop application. The set of effects supported by your game is known as your "Effect Pack." Effect packs are written in C# and loaded into the Crowd Control desktop app. The desktop app then communicates with your game which receives messages through the receiver library.
Testing of effect packs is generally performed using the Crowd Control SDK.
Maven
Add to the dependencies
section of your pom.xml
file:
<dependency>
<groupId>dev.qixils.crowdcontrol</groupId>
<artifactId>crowd-control-receiver</artifactId>
<version>3.9.1</version>
</dependency>
Gradle
Add to the dependencies
section of your build.gradle
file:
compileOnly 'dev.qixils.crowdcontrol:crowd-control-receiver:3.9.1'
Or, if using Kotlin (build.gradle.kts
):
compileOnly("dev.qixils.crowdcontrol:crowd-control-receiver:3.9.1")
To start, you must create a CrowdControl class using one of the two builder methods. This differs depending on which Crowd Control connection type you wish to use. The two connection types are outlined below.
You will also need to create a .cs
file which holds a list of all of your available effects as
well as information about how the Crowd Control desktop app should connect to your application.
Example files for each connector type are provided below.
This builder corresponds with the default SimpleTCPConnector
used by Crowd Control. In this mode,
the library runs as a client, meaning it receives commands from a central server that it connects
to. This central server is (usually) the streamer's Crowd Control desktop application running on a
local port.
To use this connector type, use the Crowd Control client builder:
import dev.qixils.crowdcontrol.CrowdControl;
// [...]
String ip = this.getIP(); // this should ideally be configurable by the streamer
CrowdControl crowdControl = CrowdControl.client()
.ip(ip)
.port(58429) // this should match what's in your .cs file
.build();
You must also use the SimpleTCPConnector
type inside your effect pack's C# file that gets loaded
into the Crowd Control app. Example:
using System;
using System.Collections.Generic;
using CrowdControl.Common;
namespace CrowdControl.Games.Packs
{
public class VideoGameNameHere : SimpleTCPPack
{
public override string Host => "127.0.0.1";
public override ushort Port => 58429;
public VideoGameNameHere(IPlayer player, Func<CrowdControlBlock, bool> responseHandler, Action<object> statusUpdateHandler) : base(player, responseHandler, statusUpdateHandler) { }
// The first three fields of the Game constructor will be assigned to you by the Crowd Control staff.
// For more information, join the Discord server: https://discord.gg/DhnfpEqmtn
public override Game Game => new Game(999, "Video Game Name Here", "VideoGameNameHere", "PC", ConnectorType.SimpleTCPConnector);
// Define all effects used in your effect pack here
public override List<Effect> Effects => new List<Effect>
{
// define "Miscellaneous" folder of effects
new Effect("Miscellaneous", "miscellaneous", ItemKind.Folder),
// define effects inside this folder
new Effect("Display Name 1", "effect_key_1", "miscellaneous"),
new Effect("Display Name 2", "effect_key_2", "miscellaneous"),
};
}
}
This builder corresponds with the SimpleTCPClientConnector
. In this mode, the library runs as a
server, meaning it can establish connections with multiple clients (streamers) and process effects
for each streamer individually. This may be ideal for multiplayer game servers like Minecraft.
To use this connector type, use the Crowd Control server builder:
import dev.qixils.crowdcontrol.CrowdControl;
// [...]
String password = this.getPassword(); // this should ideally be configurable by the server host
CrowdControl crowdControl = CrowdControl.server()
.password(password)
.port(58429) // this should match what's in your .cs file
.build();
You must also define several variables inside your effect pack's C# file that gets loaded into the Crowd Control app. Example:
using System;
using System.Collections.Generic;
using ConnectorLib;
using CrowdControl.Common;
using CrowdControl.Games.Packs;
using ConnectorType = CrowdControl.Common.ConnectorType;
namespace CrowdControl.Games.Packs
{
public class VideoGameNameHere : SimpleTCPPack<SimpleTCPClientConnector>
{
public override string Host => "127.0.0.1";
public override ushort Port => 58429;
public override ISimpleTCPPack.AuthenticationType AuthenticationMode => ISimpleTCPPack.AuthenticationType.SimpleTCPSendKey;
public override ISimpleTCPPack.DigestAlgorithm AuthenticationHashMode => ISimpleTCPPack.DigestAlgorithm.SHA_512;
public VideoGameNameHere(IPlayer player, Func<CrowdControlBlock, bool> responseHandler, Action<object> statusUpdateHandler) : base(player, responseHandler, statusUpdateHandler) { }
// The first three fields of the Game constructor will be assigned to you by the Crowd Control staff.
// For more information, join the Discord server: https://discord.gg/DhnfpEqmtn
public override Game Game => new Game(999, "Video Game Name Here", "VideoGameNameHere", "PC", ConnectorType.SimpleTCPClientConnector);
// Define all effects used in your effect pack here
public override List<Effect> Effects => new List<Effect>
{
// define "Miscellaneous" folder of effects
new Effect("Miscellaneous", "miscellaneous", ItemKind.Folder),
// define effects inside this folder
new Effect("Display Name 1", "effect_key_1", "miscellaneous"),
new Effect("Display Name 2", "effect_key_2", "miscellaneous"),
};
}
}
Once you've created your CrowdControl instance, you can start registering handlers for each of your effects.
Two overloaded methods are provided for registering an effect handler. You can either provide a
Function<Request,Response>
which is a function that takes in a
Request
and returns a
Response,
or a
Consumer<Request>
which takes in a
Request
and returns nothing. As these functions are called on an asynchronous thread, the latter is usually
used for effects that require an action to be run on the main thread. It is expected that
Consumer<Request>
handlers will eventually call
Response#send
to manually issue a
Response
to the requesting client.
Those who prefer working with annotated methods can use
#registerHandlers(Object)
to automatically register effect handlers for every appropriately annotated method inside a class.
All methods that are annotated with
@Subscribe;
return
Response,
Response.Builder,
or Void (for synchronous effects that will manually call
Response#send
);
have a single parameter that accepts only
Request;
and are public will be registered as effect handlers.
For more information, please view the method's javadocs.
Checks are functions that are called every time a
Request
is received which may or may not have knowledge of the Request. They are used to prevent the
execution of effects if your game has not yet loaded or if your players have not yet connected.
These can be registered using
#registerCheck(Supplier<CheckResult>)
and
#registerCheck(Function<Request,CheckResult>)
The documentation for all classes and methods may be found here.
An example project integrating the receiver library can be found here.
The Sender library allows you to simulate the Crowd Control desktop application by sending effect requests to a video game server or clients. This was primarily developed as an internal tool to test the Receiver library, though it can be used for numerous other applications.
Maven
Add to the dependencies
section of your pom.xml
file:
<dependency>
<groupId>dev.qixils.crowdcontrol</groupId>
<artifactId>crowd-control-sender</artifactId>
<version>3.9.1</version>
</dependency>
Gradle
Add to the dependencies
section of your build.gradle
file:
compileOnly 'dev.qixils.crowdcontrol:crowd-control-sender:3.9.1'
Or, if using Kotlin (build.gradle.kts
):
compileOnly("dev.qixils.crowdcontrol:crowd-control-sender:3.9.1")
This section assumes you have read the above documentation for the Receiver library and are familiar with the two different types of TCP connectors.
To create a server for the SimpleTCPConnector
, you should instantiate a new
SimulatedServer
.
For creating a client (or several) for the SimpleTCPClientConnector
, you should instantiate a new
SimulatedClient
.
Both of these classes implement
StartableService
which holds all the important methods for working with these simulated services.
To start the service, you must call the
#start
method on your service. For clients, you may call the
#autoStart
method to automatically reconnect to the server when the connection is lost.
Requests may be dispatched to connected services using the
#sendRequest
method. For the server, this will return a Flux of Fluxes of Responses which correspond to the
Responses received from the connected clients. The client will return a single Flux of Responses.
The Response Fluxes will emit a Response each time one corresponding to the Request is received from the connected service, and it will complete upon a Response indicating that the effect has finished executing. It may also error if the effect times out or the connected service disconnects before completion.
Before sending Requests, you should ensure that the effect being requested is known to be available
by checking the result of
#isEffectAvailable.
If the server states that an effect does not exist, then it will be marked as unavailable and the
#sendRequest
method will throw an
EffectUnavailableException
if you try to use it.
The documentation for all classes and methods may be found here.