/* * This file is part of LanternServer, licensed under the MIT License (MIT). * * Copyright (c) LanternPowered <https://www.lanternpowered.org> * Copyright (c) SpongePowered <https://www.spongepowered.org> * Copyright (c) contributors * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the Software), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package org.lanternpowered.server.network.channel; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import static org.lanternpowered.server.util.Conditions.checkNotNullOrEmpty; import static org.lanternpowered.server.util.Conditions.checkPlugin; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; import org.lanternpowered.server.entity.living.player.LanternPlayer; import org.lanternpowered.server.network.NetworkSession; import org.lanternpowered.server.network.buffer.ByteBuffer; import org.lanternpowered.server.network.buffer.ByteBufferAllocator; import org.lanternpowered.server.network.message.Message; import org.lanternpowered.server.network.vanilla.message.type.play.MessagePlayInOutChannelPayload; import org.lanternpowered.server.network.vanilla.message.type.play.MessagePlayInOutRegisterChannels; import org.spongepowered.api.Platform; import org.spongepowered.api.Server; import org.spongepowered.api.entity.living.player.Player; import org.spongepowered.api.network.ChannelBinding; import org.spongepowered.api.network.ChannelBinding.IndexedMessageChannel; import org.spongepowered.api.network.ChannelRegistrar; import org.spongepowered.api.network.ChannelRegistrationException; import org.spongepowered.api.network.RemoteConnection; import org.spongepowered.api.plugin.PluginContainer; import java.util.Iterator; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Consumer; public final class LanternChannelRegistrar implements ChannelRegistrar { private final Map<String, LanternChannelBinding> bindings = new ConcurrentHashMap<>(); private final Server server; public LanternChannelRegistrar(Server server) { this.server = server; } @Override public IndexedMessageChannel createChannel(Object plugin, String channel) throws ChannelRegistrationException { return (IndexedMessageChannel) create(plugin, channel, false); } @Override public LanternRawDataChannel createRawChannel(Object plugin, String channel) throws ChannelRegistrationException { return (LanternRawDataChannel) create(plugin, channel, true); } @Override public Optional<ChannelBinding> getChannel(String channel) { checkNotNull(channel, "channel"); return Optional.ofNullable(this.bindings.get(channel)); } private LanternChannelBinding create(Object plugin, String channel, boolean rawChannel) throws ChannelRegistrationException { final PluginContainer container = checkPlugin(plugin, "plugin"); checkNotNullOrEmpty(channel, "channel"); checkArgument(channel.length() <= 20, "channel length may not be longer then 20"); if (!isChannelAvailable(channel)) { throw new ChannelRegistrationException("Channel with name \"" + channel + "\" is already registered!"); } final LanternChannelBinding binding; if (rawChannel) { binding = new LanternRawDataChannel(this, channel, container); } else { binding = new LanternIndexedMessageChannel(this, channel, container); } binding.bound = true; final MessagePlayInOutRegisterChannels message = new MessagePlayInOutRegisterChannels(Sets.newHashSet(channel)); for (Player player : this.server.getOnlinePlayers()) { ((NetworkSession) player.getConnection()).send(message); } return binding; } @Override public void unbindChannel(ChannelBinding channel) { final LanternChannelBinding binding = (LanternChannelBinding) checkNotNull(channel, "channel"); if (binding.bound) { binding.bound = false; this.bindings.remove(channel.getName()); } } @Override public Set<String> getRegisteredChannels(Platform.Type side) { return ImmutableSet.copyOf(this.bindings.keySet()); } @Override public boolean isChannelAvailable(String channelName) { return !(this.bindings.containsKey(channelName) || channelName.startsWith("MC|") || channelName.startsWith("\001") || channelName.startsWith("FML") || channelName.equals("REGISTER") || channelName.equals("UNREGISTER")); } void sendPayload(Player player, String channel, Consumer<ByteBuffer> payload) { checkNotNull(player, "player"); checkNotNull(payload, "payload"); final NetworkSession session = ((LanternPlayer) player).getConnection(); if (session.getRegisteredChannels().contains(channel)) { final ByteBuffer buf = ByteBufferAllocator.unpooled().buffer(); payload.accept(buf); session.send(new MessagePlayInOutChannelPayload(channel, buf)); } } void sendPayloadToAll(String channel, Consumer<ByteBuffer> payload) { checkNotNull(payload, "payload"); final Iterator<Player> players = this.server.getOnlinePlayers().stream().filter( player -> ((LanternPlayer) player).getConnection().getRegisteredChannels().contains(channel)).iterator(); if (players.hasNext()) { final ByteBuffer buf = ByteBufferAllocator.unpooled().buffer(); payload.accept(buf); final Message msg = new MessagePlayInOutChannelPayload(channel, buf); players.forEachRemaining(player -> ((LanternPlayer) player).getConnection().send(msg)); } } public void handlePlayload(ByteBuffer buf, String channel, RemoteConnection connection) { final LanternChannelBinding binding = this.bindings.get(channel); if (binding != null) { binding.handlePayload(buf, connection); } } }