L2JMobius

Multi chat handlers

quasilyte · 1 · 336

Offline quasilyte

  • Heir
  • **
    • Posts: 15
I am working on plugin-like code for the l2jmobius. It needs to be as easy integratable as possible (e.g. no need to patch up the existing server files).

I found that it is hard to get multiple chat handlers working as there is only 1 handler spot per chat type. Imagine if we want to have an extra chat handler provided by a plugin/server extension - it would have to call the "original" handler after it's done. The problem is that it won't work if there are more than 1 such plugins - they might not know about each other, so one of them ends up taking it for itself.

There is a relatively simple fix for it - introduce a multi handler for chats.

Here is a file I am using:

Code: [Select]
/*
 * This file is part of the L2J Mobius project.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
package org.l2jmobius.gameserver.handler;

import java.util.ArrayList;
import java.util.EnumMap;
import java.util.Map;

import org.l2jmobius.gameserver.model.actor.Player;
import org.l2jmobius.gameserver.network.enums.ChatType;

/**
 * This class handles all chat handlers
 * @author durgus
 */
public class ChatHandler implements IHandler<IChatHandler, ChatType>
{
private final Map<ChatType, MultiHandler> _datatable = new EnumMap<>(ChatType.class);

private class MultiHandler implements IChatHandler {
public ArrayList<IChatHandler> list;

public MultiHandler() {
list = new ArrayList<>();
list.ensureCapacity(1);
}

[member=79]override[/member]
public void handleChat(ChatType type, Player player, String target, String text) {
for (IChatHandler h : list) {
h.handleChat(type, player, target, text);
}
}

[member=79]override[/member]
public ChatType[] getChatTypeList() {
// This method is only called when registering/unloading
// handlers, so there is hardly any reason to call it
// on a multi-handler.
switch (list.size()) {
case 0:
return null;
case 1:
return list.getFirst().getChatTypeList();
}
throw new UnsupportedOperationException();
}
}

/**
* Singleton constructor
*/
protected ChatHandler()
{
}

/**
* Register a new chat handler
* @param handler
*/
[member=79]override[/member]
public void registerHandler(IChatHandler handler)
{
for (ChatType type : handler.getChatTypeList())
{
MultiHandler multi = _datatable.get(type);
if (multi == null) {
multi = new MultiHandler();
_datatable.put(type, multi);
}
multi.list.add(handler);
}
}

[member=79]override[/member]
public synchronized void removeHandler(IChatHandler handler)
{
for (ChatType type : handler.getChatTypeList())
{
MultiHandler multi = _datatable.get(type);
if (multi == null) {
continue;
}
multi.list.remove(handler);
}
}

/**
* Get the chat handler for the given chat type
* @param chatType
* [member=10368]return[/member]
*/
[member=79]override[/member]
public IChatHandler getHandler(ChatType chatType)
{
return _datatable.get(chatType);
}

/**
* Returns the size
* [member=10368]return[/member]
*/
[member=79]override[/member]
public int size()
{
return _datatable.size();
}

public static ChatHandler getInstance()
{
return SingletonHolder.INSTANCE;
}

private static class SingletonHolder
{
protected static final ChatHandler INSTANCE = new ChatHandler();
}
}

In form of a patch:
Code: [Select]
/gameserver/handler/ChatHandler.java
index 15c5d870601..67fb998cea5 100644
--- a/L2J_Mobius_CT_0_Interlude/java/org/l2jmobius/gameserver/handler/ChatHandler.java
+++ b/L2J_Mobius_CT_0_Interlude/java/org/l2jmobius/gameserver/handler/ChatHandler.java
@@ -16,9 +16,11 @@
  */
 package org.l2jmobius.gameserver.handler;
 
+import java.util.ArrayList;
 import java.util.EnumMap;
 import java.util.Map;
 
+import org.l2jmobius.gameserver.model.actor.Player;
 import org.l2jmobius.gameserver.network.enums.ChatType;
 
 /**
@@ -27,7 +29,37 @@ import org.l2jmobius.gameserver.network.enums.ChatType;
  */
 public class ChatHandler implements IHandler<IChatHandler, ChatType>
 {
- private final Map<ChatType, IChatHandler> _datatable = new EnumMap<>(ChatType.class);
+ private final Map<ChatType, MultiHandler> _datatable = new EnumMap<>(ChatType.class);
+
+ private class MultiHandler implements IChatHandler {
+ public ArrayList<IChatHandler> list;
+
+ public MultiHandler() {
+ list = new ArrayList<>();
+ list.ensureCapacity(1);
+ }
+
+ [member=79]override[/member]
+ public void handleChat(ChatType type, Player player, String target, String text) {
+ for (IChatHandler h : list) {
+ h.handleChat(type, player, target, text);
+ }
+ }
+
+ [member=79]override[/member]
+ public ChatType[] getChatTypeList() {
+ // This method is only called when registering/unloading
+ // handlers, so there is hardly any reason to call it
+ // on a multi-handler.
+ switch (list.size()) {
+ case 0:
+ return null;
+ case 1:
+ return list.getFirst().getChatTypeList();
+ }
+ throw new UnsupportedOperationException();
+ }
+ }
 
  /**
  * Singleton constructor
@@ -45,7 +77,12 @@ public class ChatHandler implements IHandler<IChatHandler, ChatType>
  {
  for (ChatType type : handler.getChatTypeList())
  {
- _datatable.put(type, handler);
+ MultiHandler multi = _datatable.get(type);
+ if (multi == null) {
+ multi = new MultiHandler();
+ _datatable.put(type, multi);
+ }
+ multi.list.add(handler);
  }
  }
 
@@ -54,7 +91,11 @@ public class ChatHandler implements IHandler<IChatHandler, ChatType>
  {
  for (ChatType type : handler.getChatTypeList())
  {
- _datatable.remove(type);
+ MultiHandler multi = _datatable.get(type);
+ if (multi == null) {
+ continue;
+ }
+ multi.list.remove(handler);
  }
  }
 
@@ -88,4 +129,4 @@ public class ChatHandler implements IHandler<IChatHandler, ChatType>
  {
  protected static final ChatHandler INSTANCE = new ChatHandler();
  }
-}
\ No newline at end of file
+}

I tested it with a 1 (original) and 2 chat handlers - it seem to be working fine.

If you like the idea, could you incorporate something like that in the core? I do think that having an ability to make non-intrusive plugins/extensions to the server is a very good thing for the project.

If you don't like it, that's alright too. I am interested to know if there are better ways to handle the chat without modifying the core server files.