From 4f18d37832d32c672c2ffd691bc7ed24414e3323 Mon Sep 17 00:00:00 2001 From: Pasqual Koschmieder Date: Thu, 30 Jun 2022 01:08:14 +0200 Subject: [PATCH] fix exceptions when reloading the server (#1689) --- .../injector/netty/manager/ListeningList.java | 166 +++++++++++++----- .../netty/manager/NetworkManagerInjector.java | 10 +- 2 files changed, 130 insertions(+), 46 deletions(-) diff --git a/src/main/java/com/comphenix/protocol/injector/netty/manager/ListeningList.java b/src/main/java/com/comphenix/protocol/injector/netty/manager/ListeningList.java index fb4528cf..eca6a34d 100644 --- a/src/main/java/com/comphenix/protocol/injector/netty/manager/ListeningList.java +++ b/src/main/java/com/comphenix/protocol/injector/netty/manager/ListeningList.java @@ -4,10 +4,14 @@ import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelHandler; import java.util.Collection; +import java.util.Comparator; import java.util.Iterator; import java.util.List; import java.util.ListIterator; import java.util.NoSuchElementException; +import java.util.function.Consumer; +import java.util.function.Predicate; +import java.util.function.UnaryOperator; @SuppressWarnings("NullableProblems") final class ListeningList implements List { @@ -25,17 +29,23 @@ final class ListeningList implements List { @Override public int size() { - return this.original.size(); + synchronized (this) { + return this.original.size(); + } } @Override public boolean isEmpty() { - return this.original.isEmpty(); + synchronized (this) { + return this.original.isEmpty(); + } } @Override public boolean contains(Object o) { - return this.original.contains(o); + synchronized (this) { + return this.original.contains(o); + } } @Override @@ -43,81 +53,113 @@ final class ListeningList implements List { return this.original.iterator(); } + @Override + public void forEach(Consumer action) { + synchronized (this) { + this.original.forEach(action); + } + } + @Override public Object[] toArray() { - return this.original.toArray(); + synchronized (this) { + return this.original.toArray(); + } } @Override public T[] toArray(T[] a) { - //noinspection SuspiciousToArrayCall - return this.original.toArray(a); + synchronized (this) { + //noinspection SuspiciousToArrayCall + return this.original.toArray(a); + } } @Override public boolean add(Object o) { - this.processInsert(o); - return this.original.add(o); + synchronized (this) { + this.processInsert(o); + return this.original.add(o); + } } @Override public void add(int index, Object element) { - this.processInsert(element); - this.original.add(index, element); + synchronized (this) { + this.processInsert(element); + this.original.add(index, element); + } } @Override public boolean addAll(Collection c) { - c.forEach(this::processInsert); - return this.original.addAll(c); + synchronized (this) { + c.forEach(this::processInsert); + return this.original.addAll(c); + } } @Override public boolean addAll(int index, Collection c) { - c.forEach(this::processInsert); - return this.original.addAll(index, c); + synchronized (this) { + c.forEach(this::processInsert); + return this.original.addAll(index, c); + } } @Override public Object set(int index, Object element) { - this.processInsert(element); + synchronized (this) { + this.processInsert(element); - Object prev = this.original.set(index, element); - this.processRemove(prev); + Object prev = this.original.set(index, element); + this.processRemove(prev); - return prev; + return prev; + } } @Override public boolean remove(Object o) { - if (this.original.remove(o)) { - this.processRemove(o); - return true; - } + synchronized (this) { + if (this.original.remove(o)) { + this.processRemove(o); + return true; + } - return false; + return false; + } } @Override public boolean containsAll(Collection c) { - return this.original.containsAll(c); + synchronized (this) { + //noinspection SlowListContainsAll + return this.original.containsAll(c); + } } @Override public Object remove(int index) { - Object removed = this.original.remove(index); - this.processRemove(removed); - return removed; + synchronized (this) { + Object removed = this.original.remove(index); + this.processRemove(removed); + return removed; + } } @Override public int indexOf(Object o) { - return this.original.indexOf(o); + synchronized (this) { + return this.original.indexOf(o); + } } @Override public int lastIndexOf(Object o) { - return this.original.lastIndexOf(o); + synchronized (this) { + return this.original.lastIndexOf(o); + } } @Override @@ -132,33 +174,77 @@ final class ListeningList implements List { @Override public List subList(int fromIndex, int toIndex) { - return this.original.subList(fromIndex, toIndex); + synchronized (this) { + return this.original.subList(fromIndex, toIndex); + } } @Override public boolean removeAll(Collection c) { - c.forEach(element -> { - if (this.original.contains(element)) { - this.processRemove(element); - } - }); - return this.original.removeAll(c); + synchronized (this) { + c.forEach(element -> { + if (this.original.contains(element)) { + this.processRemove(element); + } + }); + return this.original.removeAll(c); + } + } + + @Override + public boolean removeIf(Predicate filter) { + synchronized (this) { + return this.original.removeIf(object -> { + boolean shouldRemove = filter.test(object); + if (shouldRemove) { + this.processRemove(object); + } + return shouldRemove; + }); + } } @Override public boolean retainAll(Collection c) { - return this.original.retainAll(c); + synchronized (this) { + return this.original.retainAll(c); + } + } + + @Override + public void replaceAll(UnaryOperator operator) { + synchronized (this) { + this.original.replaceAll(value -> { + Object newValue = operator.apply(value); + if (newValue != value) { + this.processRemove(value); + this.processInsert(newValue); + } + return newValue; + }); + } + } + + @Override + public void sort(Comparator c) { + synchronized (this) { + this.original.sort(c); + } } @Override public void clear() { - this.original.forEach(this::processRemove); - this.original.clear(); + synchronized (this) { + this.original.forEach(this::processRemove); + this.original.clear(); + } } @Override public Object get(int index) { - return this.original.get(index); + synchronized (this) { + return this.original.get(index); + } } private void processInsert(Object element) { diff --git a/src/main/java/com/comphenix/protocol/injector/netty/manager/NetworkManagerInjector.java b/src/main/java/com/comphenix/protocol/injector/netty/manager/NetworkManagerInjector.java index 88e0b132..1bcc687f 100644 --- a/src/main/java/com/comphenix/protocol/injector/netty/manager/NetworkManagerInjector.java +++ b/src/main/java/com/comphenix/protocol/injector/netty/manager/NetworkManagerInjector.java @@ -26,7 +26,6 @@ import io.netty.channel.ChannelFuture; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; -import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -205,7 +204,7 @@ public class NetworkManagerInjector implements ChannelListener { //noinspection SynchronizationOnLocalVariableOrMethodParameter synchronized (value) { // override the list field with our list - List newList = Collections.synchronizedList(new ListeningList(value, this.pipelineInjectorHandler)); + List newList = new ListeningList(value, this.pipelineInjectorHandler); accessor.set(serverConnection, newList); } } @@ -215,7 +214,6 @@ public class NetworkManagerInjector implements ChannelListener { this.injected = true; } - @SuppressWarnings("unchecked") public void close() { if (this.closed || !this.injected) { return; @@ -229,10 +227,10 @@ public class NetworkManagerInjector implements ChannelListener { for (Pair list : this.overriddenLists) { // get the value of the field we've overridden, if it is no longer a ListeningList someone probably jumped in // and replaced the field himself - we are out safely as the other person needs to clean the mess... - List value = (List) list.getSecond().get(list.getFirst()); - if (value instanceof ListeningList) { + Object currentFieldValue = list.getSecond().get(list.getFirst()); + if (currentFieldValue instanceof ListeningList) { // just reset to the list we wrapped originally - ListeningList ourList = (ListeningList) value; + ListeningList ourList = (ListeningList) currentFieldValue; List original = ourList.getOriginal(); //noinspection SynchronizationOnLocalVariableOrMethodParameter synchronized (original) {