Class OsuRoom
Represents a multiplayer lobby in osu!
Automatically does ratelimiting by not sending more than a message every 2 seconds.
All slot indices are 0 based.
Properties
Name | Type | Description |
channel [get]
|
string | Returns the channel name as on IRC
|
host [set]
|
string | Gives host to a player
|
locked [set]
|
bool | Property to lock slots (disallow changing slots & joining)
|
map [set]
|
string | Changes the map to a beatmap ID (b/ url)
|
mods [set]
|
Mod[] | Changes the mods in this lobby (pass FreeMod first if you want FreeMod)
|
mpid [get]
|
string | Returns the game ID as usable in osu://mp/ID urls
|
password [set]
|
string | Sets the match password (password will be visible to existing players)
|
room [get]
|
string | Returns the room ID as usable in the mp history URL or IRC joinable via #mp_ID
|
settings [get]
|
OsuRoom.Settings | Returns the current mp settings
|
size [set]
|
ubyte | Changes the slot limit of this lobby
|
Methods
Name | Description |
abortMatch
|
Aborts a running match
|
abortTimer
|
Aborts any running countdown
|
clearhost
|
Makes nobody host (make it system/bog managed)
|
close
|
Closes the room
|
invite
|
Invites a player to the room
|
kick
|
Kicks a player from the room
|
move
|
Moves a player to another slot
|
processClosed
|
Processes a room closed event
|
processFinishPlaying
|
Processes a user finish playing event & updates the state
|
processHost
|
Processes a user host event & updates the state
|
processJoin
|
Processes a user join event & updates the state
|
processLeave
|
Processes a user leave event & updates the state
|
processMatchFinish
|
Processes a match end event & updates the state
|
processMove
|
Processes a user move event & updates the state
|
processSize
|
Processes a room size change event & updates the state
|
processTeam
|
Processes a user team switch event & updates the state
|
ratelimit
|
Manually wait until you can send a message again
|
sendMessage
|
Sends a message with a 2 second ratelimit
|
set
|
Sets up teammode, scoremode & lobby size
|
setTeam
|
Changes a user's team
|
setTimer
|
Sets a timer using !mp timer
|
start
|
Starts a match after a specified amount of seconds. If after is <= 0 the game will be started immediately.
The timeout can be canceled using abortTimer.
|
waitForJoin
|
Waits for a player to join the room & return the username
|
waitForTimer
|
Waits for an existing timer/countdown to finish (wont start one)
|
Inner structs
Name | Description |
Settings
|
Returned by !mp settings
|
Example
BanchoBot banchoConnection = new BanchoBot("WebFreak", "");
bool running = true;
auto botTask = runTask({
while (running)
{
banchoConnection.connect();
logDiagnostic("Got disconnected from bancho...");
sleep(2.seconds);
}
});
sleep(3.seconds);
auto users = ["WebFreak", "Node"];
OsuRoom room = banchoConnection.createRoom("bob");
runTask({
foreach (user; users)
room.invite(user);
});
runTask({
room.password = "123456";
room.size = 8;
room.mods = [Mod.Hidden, Mod.DoubleTime];
room.map = "1158325";
});
runTask({
int joined;
try
{
while (true)
{
string user = room.waitForJoin(30.seconds);
joined++;
room.sendMessage("yay welcome " ~ user ~ "!", HighPriority.yes);
}
}
catch (InterruptException)
{
if (joined == 0)
{
// forever alone
room.close();
return;
}
}
room.sendMessage("This is an automated test, this room will close in 10 seconds on timer");
room.setTimer(10.seconds);
try
{
room.waitForTimer(15.seconds);
}
catch (InterruptException)
{
room.sendMessage("Timer didn't trigger :(");
room.sendMessage("closing the room in 5s");
sleep(5.seconds);
}
room.close();
}).join();
running = false;
banchoConnection.disconnect();
botTask.join();
Example
Simple auto host rotate bot
import std.range;
BanchoBot banchoConnection = new BanchoBot("WebFreak", "");
bool running = true;
auto botTask = runTask({
while (running)
{
banchoConnection.connect();
logDiagnostic("Got disconnected from bancho...");
sleep(2.seconds);
}
});
sleep(3.seconds);
OsuRoom room = banchoConnection.createRoom("4*+ auto host rotate (join time based)");
room.invite("WebFreak");
runTask({ room.password = ""; room.size = 8; room.mods = [Mod.FreeMod]; });
string[] hostOrder;
size_t currentHost;
room.onClosed ~= () {
running = false;
banchoConnection.disconnect();
botTask.join();
};
bool beatmapChosen = false;
bool choosingMap = false;
bool startedChoosingTimer = false;
Timer choosingTimer;
string nextHost(bool deleteCurrent = false)
{
if (deleteCurrent)
hostOrder = hostOrder[0 .. currentHost] ~ hostOrder[currentHost + 1 .. $];
if (hostOrder.length == 0)
{
// everyone left
//room.close();
return null;
}
else
{
if (!deleteCurrent)
currentHost = (currentHost + 1) % hostOrder.length;
else if (currentHost >= hostOrder.length)
currentHost = 0;
auto user = hostOrder[currentHost];
try
{
room.playerByName(user);
beatmapChosen = false;
choosingMap = false;
room.host = user;
if (choosingTimer && choosingTimer.pending)
choosingTimer.stop();
choosingTimer = setTimer(40.seconds, {
if (choosingMap || !hostOrder.length)
return;
room.sendMessage(
hostOrder[currentHost] ~ ": please pick a map or next host will be picked!");
room.setTimer(60.seconds);
startedChoosingTimer = true;
});
if (hostOrder.length)
room.sendMessage("Next hosts are " ~ hostOrder.cycle.drop(currentHost + 1)
.take(min(3, hostOrder.length - 1)).join(", "));
return user;
}
catch (Exception)
{
room.sendMessage("Tried to give host to " ~ user ~ " but they left?");
return nextHost(true);
}
}
}
room.onBeatmapPending ~= () {
if (beatmapChosen)
return;
if (choosingTimer && choosingTimer.pending)
choosingTimer.stop();
choosingMap = true;
if (startedChoosingTimer)
{
startedChoosingTimer = false;
room.abortTimer();
}
choosingTimer = setTimer(60.seconds, {
if (beatmapChosen || !hostOrder.length)
return;
room.sendMessage(hostOrder[currentHost] ~ ": please pick a map or next host will be picked!");
room.setTimer(30.seconds);
startedChoosingTimer = true;
});
};
room.onBeatmapChanged ~= (beatmap) {
if (choosingTimer && choosingTimer.pending)
choosingTimer.stop();
beatmapChosen = true;
};
room.onCountdownFinished ~= () {
if (!beatmapChosen)
nextHost();
};
room.onUserHost ~= (user) {
if (user != hostOrder[currentHost])
{
string s = nextHost();
if (s != user)
room.sendMessage("Host passed elsewhere, passing where it was supposed to go next");
}
};
room.onUserJoin ~= (user, slot) {
if (hostOrder.canFind(user))
return;
if (hostOrder.length)
{
// insert before current host
hostOrder = hostOrder[0 .. currentHost] ~ user ~ hostOrder[currentHost .. $];
currentHost++;
}
else
{
hostOrder = [user]; // first join!
nextHost();
}
};
room.onUserLeave ~= (user) {
if (user == hostOrder[currentHost])
nextHost(true);
};
bool startedLongTimer;
room.onBeatmapChanged ~= (beatmap) {
if (!startedLongTimer)
{
room.start(3.minutes);
startedLongTimer = true;
}
};
bool startedTimer;
room.onPlayersReady ~= () {
if (!startedTimer)
{
room.start(15.seconds);
startedTimer = true;
}
};
room.onMatchStart ~= () {
startedTimer = false;
startedLongTimer = false;
room.password = "";
};
room.onMatchEnd ~= () { nextHost(); };
botTask.join();