Removed auto-sharding. Closes #21

This commit is contained in:
Benny Jacobs 2018-11-17 19:18:40 +01:00
parent 7429576bf7
commit b263836af7
11 changed files with 48 additions and 77 deletions

View file

@ -27,8 +27,6 @@ Starting from version `1.0.0` of nyxx, library requires `Dart 2.0+`. You can use
Nyxx works on the command line, in the browser, and on mobile devices.
- **Fine Control** <br>
Nyxx allows you to control every outgoing HTTP request or WebSocket message.
- **Internal Sharding** <br>
Nyxx automatically spawns shards for your bot, but you can override this and spawn a custom number of shards. Internal sharding means that all of your bots shards are managed in one script, and there is no need for communication between shards.
- **Complete** <br>
Nyxx supports nearly all Discord API endpoints.

View file

@ -25,7 +25,7 @@ void setupBot(SendPort remotePort) {
// Listen to all incoming messages via Dart Stream
client.onMessage.listen((MessageEvent e) {
remotePort.send(
"${client.shards.length};${client.guilds.count};${client.uptime.inSeconds}");
"${client.shards};${client.guilds.count};${client.uptime.inSeconds}");
if (e.message.content == "oofping") {
e.message.channel.send(content: "Pong!");

View file

@ -7,10 +7,10 @@ Logger _logger = Logger.detached("Voice Service");
Future<void> sendFakeOp4(VoiceChannel channel,
{bool mute = false, bool deafen = false, Guild guild}) async {
if (guild != null) {
guild.shard.send(
guild.client.shard.send(
"VOICE_STATE_UPDATE", _Opcode4(guild, channel, mute, deafen)._build());
} else {
channel.guild.shard.send("VOICE_STATE_UPDATE",
channel.guild.client.shard.send("VOICE_STATE_UPDATE",
_Opcode4(channel.guild, channel, mute, deafen)._build());
}
}
@ -80,7 +80,7 @@ class VoiceService {
try {
transport.WebSocket.connect(_wsPath, headers: {
"Authorization": _password,
"Num-Shards": client.shards.length,
"Num-Shards": client.shards,
"User-Id": client.app.id.toString()
}).then((wc) {
this._webSocket = wc;

View file

@ -43,7 +43,7 @@ class Player {
_onTrackError = StreamController.broadcast();
onTrackError = _onTrackError.stream;
_guild.shard.send(
_guild.client.shard.send(
"VOICE_STATE_UPDATE", _Opcode4(_guild, channel, false, false)._build());
_currentState = (await _manager.client.onVoiceStateUpdate.first).state;
@ -81,7 +81,7 @@ class Player {
Future<void> changeChannel(VoiceChannel channel,
{bool muted = false, bool deafen = false}) {
currentChannel = channel;
_guild.shard.send("VOICE_STATE_UPDATE",
_guild.client.shard.send("VOICE_STATE_UPDATE",
_Opcode4(_guild, channel, muted, deafen)._build());
}
@ -115,7 +115,7 @@ class Player {
/// Disconnect from channel. Closes all unneeded connections.
Future<void> disconnect() async {
_guild.shard.send(
_guild.client.shard.send(
"VOICE_STATE_UPDATE", _Opcode4(_guild, null, false, false)._build());
await stop();
_manager._webSocket.add(jsonEncode(_SimpleOp("destroy", _guild)._build()));
@ -133,7 +133,7 @@ class Player {
op = _OpPause(_guild, true).build();
_manager._webSocket.add(jsonEncode(op));
_guild.shard.send(
_guild.client.shard.send(
"VOICE_STATE_UPDATE",
_Opcode4(_guild, currentChannel, !_currentState.selfMute,
_currentState.selfDeaf)

View file

@ -3,15 +3,12 @@ part of nyxx;
/// Optional client settings which can be used when creating new instance
/// of client. It allows to tune up client to your needs.
class ClientOptions {
List<int> _shardIds;
/// Whether or not to disable @everyone and @here mentions at a global level.
/// **It means client won't send any of these. It doesn't mean filtering guild messages.**
bool disableEveryone;
/// Whether or not to automatically shard the client if the default shard
/// values are untouched.
bool autoShard;
/// The index of this shard
int shardIndex;
/// The total number of shards.
int shardCount;
@ -33,15 +30,10 @@ class ClientOptions {
/// Makes a new `ClientOptions` object.
ClientOptions(
{this.disableEveryone = false,
this.autoShard = false,
this.shardIndex = 0,
this.shardCount = 1,
this.messageCacheSize = 400,
this.forceFetchMembers = false,
this.cacheMembers = true,
this.largeThreshold = 50}) {
if (!autoShard && shardCount > 1)
this._shardIds = Iterable.generate(shardCount, (i) => i).toList();
else
this._shardIds = const [0];
}
this.largeThreshold = 50});
}

View file

@ -47,9 +47,7 @@ class Nyxx implements Disposable {
/// The current version of `nyxx`
String version = _Constants.version;
/// The client's internal shards. By default shards are setup automatically by gateway,
/// however this can be changed by [ClientOptions]
Map<int, Shard> shards;
Shard shard;
/// Generic Stream for message like events. It includes added reactions, and message deletions.
/// For received messages refer to [onMessageReceived]
@ -218,7 +216,6 @@ class Nyxx implements Disposable {
this.guilds = _SnowflakeCache();
this.channels = ChannelCache._new();
this.users = _SnowflakeCache();
this.shards = Map<int, Shard>();
this._http = Http._new(this);
this._events = _EventController(this);
@ -329,12 +326,13 @@ class Nyxx implements Disposable {
}
/// Closes websocket connections and cleans everything up.
Future<void> close() async => dispose();
Future<void> close() async => await dispose();
int get shards => this._options.shardCount;
@override
Future<void> dispose() async {
//for (var shard in this.shards.values) await shard._socket.close(1000);
await shard.dispose();
await guilds.dispose();
await users.dispose();
await guilds.dispose();

View file

@ -4,7 +4,7 @@ part of nyxx;
/// Guild sharding is entirely user controlled, and requires no state-sharing between separate connections to operate.
///
/// Shard is basically represents single websocket connection to gateway. Each shard can operate on up to 2500 guilds.
class Shard {
class Shard implements Disposable {
/// The shard id.
int id;
@ -27,6 +27,7 @@ class Shard {
String _sessionId;
StreamController<Shard> _onReady;
StreamController<Shard> _onDisconnect;
bool _reconnect = true;
Logger _logger = Logger("Websocket");
@ -372,12 +373,7 @@ class Shard {
this._heartbeatTimer.cancel();
_logger.severe(
"Shard [$id] disconnected. Error code: [${this._socket.closeCode}] | Error message: [${this._socket.closeReason}]");
/*if (this._socket.closeCode == null) {
_logger.severe("Exitting. Null close code");
exit(1);
}*/
/// Dispose on error
for (var guild in this.guilds.values) {
guild.dispose();
@ -390,10 +386,12 @@ class Shard {
break;
case 4007:
case 4009:
this._connect(true);
if(this._reconnect)
this._connect(true);
break;
default:
Timer(const Duration(seconds: 2), () => this._connect(false, true));
if(this._reconnect)
Timer(const Duration(seconds: 2), () => this._connect(false, true));
break;
}
@ -401,4 +399,11 @@ class Shard {
.add(DisconnectEvent._new(this, this._socket.closeCode));
this._onDisconnect.add(this);
}
@override
Future<void> dispose() async {
this._reconnect = false;
await this._socket.close(1000);
return Null;
}
}

View file

@ -14,24 +14,14 @@ class _WS {
_client._http._headers['Authorization'] = "Bot ${_client._token}";
_client._http.send("GET", "/gateway/bot").then((HttpResponse r) {
this.gateway = r.body['url'] as String;
if (_client._options.autoShard) {
_client._options._shardIds = [];
_client._options.shardCount = r.body['shards'] as int;
for (int i = 0; i < _client._options.shardCount; i++) {
setupShard(i);
}
} else {
for (int shardId in _client._options._shardIds) {
setupShard(shardId);
}
}
setupShard(_client._options.shardIndex);
this.connectShard(0);
});
}
void setupShard(int shardId) {
Shard shard = Shard._new(this, shardId);
_client.shards[shard.id] = shard;
_client.shard = shard;
shard.onReady.listen((Shard s) {
if (!_client.ready) {
@ -41,9 +31,7 @@ class _WS {
}
void connectShard(int index) {
_client.shards.values.toList()[index]._connect(false, true);
if (index + 1 != _client._options._shardIds.length)
Timer(Duration(seconds: 6), () => connectShard(index + 1));
_client.shard._connect(false, true);
}
void testReady() {
@ -59,10 +47,8 @@ class _WS {
}
bool match2() {
for (var shard in _client.shards.values) {
if (!shard.ready) {
return false;
}
if (!_client.shard.ready) {
return false;
}
return true;

View file

@ -30,14 +30,11 @@ class ClientUser extends User {
/// Updates the client's presence
ClientUser setPresence(
{String status, bool afk = false, Presence game, DateTime since}) {
client.shards.forEach((int id, Shard shard) {
shard.setPresence(status: status, afk: afk, game: game, since: since);
});
client.shard.setPresence(status: status, afk: afk, game: game, since: since);
return this;
}
/// Allows to set status for each shard based on shard state.
/// Allows to set status for shard based on shard state.
///
/// ```
/// client.setPresenceForShard((shard) {
@ -45,7 +42,7 @@ class ClientUser extends User {
/// });
/// ```
void setPresenceForShard(Function(Shard shard) func) {
for (var shard in client.shards.values) func(shard);
func(client.shard);
}
/// Allows to get [Member] objects for all guilds for bot user.

View file

@ -79,9 +79,6 @@ class Guild extends SnowflakeEntity implements Disposable, Nameable {
/// Permission of current(bot) user in this guild
Permissions currentUserPermissions;
/// The shard that the guild is on.
Shard shard;
/// Users state cache
Cache<Snowflake, VoiceState> voiceStates;
@ -124,9 +121,6 @@ class Guild extends SnowflakeEntity implements Disposable, Nameable {
});
}
this.shard = client.shards[
(int.parse(this.id.toString()) >> 22) % client._options.shardCount];
if (guildCreate) {
this.members = _SnowflakeCache();
this.channels = ChannelCache._new();
@ -591,13 +585,14 @@ class Guild extends SnowflakeEntity implements Disposable, Nameable {
}
@override
Future<void> dispose() => Future(() {
channels.dispose();
members.dispose();
roles.dispose();
emojis.dispose();
voiceStates.dispose();
});
Future<Null> dispose() async {
await channels.dispose();
await members.dispose();
await roles.dispose();
await emojis.dispose();
await voiceStates.dispose();
return null;
}
@override
String get nameString => "Guild ${this.name} [${this.id}]";

View file

@ -128,7 +128,7 @@ void main() {
assert(bot.channels.count > 0);
assert(bot.users.count > 0);
assert(bot.shards.length == 1);
assert(bot.shards == 1);
assert(bot.ready);
assert(bot.inviteLink != null);