mirror of
https://github.com/PaperMC/Waterfall.git
synced 2024-09-30 07:27:28 +02:00
parent
a1e3ee4799
commit
124a8b6213
@ -1,4 +1,4 @@
|
|||||||
From 84c2a7140f99bb3a77027e8c845772f021f7b7b7 Mon Sep 17 00:00:00 2001
|
From c8a58f65efa67b17ee0c2e8b44411395e1bbcb40 Mon Sep 17 00:00:00 2001
|
||||||
From: Techcable <Techcable@techcable.net>
|
From: Techcable <Techcable@techcable.net>
|
||||||
Date: Thu, 19 May 2016 10:55:20 -0700
|
Date: Thu, 19 May 2016 10:55:20 -0700
|
||||||
Subject: [PATCH] Configurable Waterfall Metrics
|
Subject: [PATCH] Configurable Waterfall Metrics
|
||||||
@ -17,143 +17,46 @@ index b30541b..293ec4e 100644
|
|||||||
+ */
|
+ */
|
||||||
+ boolean isMetrics();
|
+ boolean isMetrics();
|
||||||
}
|
}
|
||||||
diff --git a/proxy/src/main/java/io/github/waterfallmc/waterfall/Metrics.java b/proxy/src/main/java/io/github/waterfallmc/waterfall/Metrics.java
|
diff --git a/proxy/src/main/java/net/md_5/bungee/Metrics.java b/proxy/src/main/java/io/github/waterfallmc/waterfall/Metrics.java
|
||||||
new file mode 100644
|
similarity index 96%
|
||||||
index 0000000..ae5a2a9
|
rename from proxy/src/main/java/net/md_5/bungee/Metrics.java
|
||||||
--- /dev/null
|
rename to proxy/src/main/java/io/github/waterfallmc/waterfall/Metrics.java
|
||||||
|
index 9523987..ae5a2a9 100644
|
||||||
|
--- a/proxy/src/main/java/net/md_5/bungee/Metrics.java
|
||||||
+++ b/proxy/src/main/java/io/github/waterfallmc/waterfall/Metrics.java
|
+++ b/proxy/src/main/java/io/github/waterfallmc/waterfall/Metrics.java
|
||||||
@@ -0,0 +1,131 @@
|
@@ -1,4 +1,4 @@
|
||||||
|
-package net.md_5.bungee;
|
||||||
+package io.github.waterfallmc.waterfall;
|
+package io.github.waterfallmc.waterfall;
|
||||||
+
|
|
||||||
+import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
+import java.io.IOException;
|
import java.io.IOException;
|
||||||
+import java.io.InputStreamReader;
|
@@ -9,6 +9,8 @@ import java.net.URL;
|
||||||
+import java.io.OutputStreamWriter;
|
import java.net.URLConnection;
|
||||||
+import java.io.UnsupportedEncodingException;
|
import java.net.URLEncoder;
|
||||||
+import java.net.URL;
|
import java.util.TimerTask;
|
||||||
+import java.net.URLConnection;
|
|
||||||
+import java.net.URLEncoder;
|
|
||||||
+import java.util.TimerTask;
|
|
||||||
+
|
+
|
||||||
+import net.md_5.bungee.BungeeCord;
|
+import net.md_5.bungee.BungeeCord;
|
||||||
+import net.md_5.bungee.api.ProxyServer;
|
import net.md_5.bungee.api.ProxyServer;
|
||||||
+
|
|
||||||
+public class Metrics extends TimerTask
|
public class Metrics extends TimerTask
|
||||||
+{
|
@@ -29,7 +31,7 @@ public class Metrics extends TimerTask
|
||||||
+
|
/**
|
||||||
+ /**
|
* Interval of time to ping (in minutes)
|
||||||
+ * The current revision number
|
*/
|
||||||
+ */
|
- final static int PING_INTERVAL = 10;
|
||||||
+ private final static int REVISION = 5;
|
|
||||||
+ /**
|
|
||||||
+ * The base url of the metrics domain
|
|
||||||
+ */
|
|
||||||
+ private static final String BASE_URL = "http://mcstats.org";
|
|
||||||
+ /**
|
|
||||||
+ * The url used to report a server's status
|
|
||||||
+ */
|
|
||||||
+ private static final String REPORT_URL = "/report/%s";
|
|
||||||
+ /**
|
|
||||||
+ * Interval of time to ping (in minutes)
|
|
||||||
+ */
|
|
||||||
+ public final static int PING_INTERVAL = 10;
|
+ public final static int PING_INTERVAL = 10;
|
||||||
+ boolean firstPost = true;
|
boolean firstPost = true;
|
||||||
+
|
|
||||||
+ @Override
|
@Override
|
||||||
+ public void run()
|
@@ -71,7 +73,7 @@ public class Metrics extends TimerTask
|
||||||
+ {
|
}
|
||||||
+ try
|
|
||||||
+ {
|
// Create the url
|
||||||
+ // We use the inverse of firstPost because if it is the first time we are posting,
|
- URL url = new URL( BASE_URL + String.format( REPORT_URL, encode( "BungeeCord" ) ) );
|
||||||
+ // it is not a interval ping, so it evaluates to FALSE
|
|
||||||
+ // Each time thereafter it will evaluate to TRUE, i.e PING!
|
|
||||||
+ postPlugin( !firstPost );
|
|
||||||
+
|
|
||||||
+ // After the first post we set firstPost to false
|
|
||||||
+ // Each post thereafter will be a ping
|
|
||||||
+ firstPost = false;
|
|
||||||
+ } catch ( IOException ex )
|
|
||||||
+ {
|
|
||||||
+ // ProxyServer.getInstance().getLogger().info( "[Metrics] " + ex.getMessage() );
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ /**
|
|
||||||
+ * Generic method that posts a plugin to the metrics website
|
|
||||||
+ */
|
|
||||||
+ private void postPlugin(boolean isPing) throws IOException
|
|
||||||
+ {
|
|
||||||
+ // Construct the post data
|
|
||||||
+ final StringBuilder data = new StringBuilder();
|
|
||||||
+ data.append( encode( "guid" ) ).append( '=' ).append( encode( BungeeCord.getInstance().config.getUuid() ) );
|
|
||||||
+ encodeDataPair( data, "version", ProxyServer.getInstance().getVersion() );
|
|
||||||
+ encodeDataPair( data, "server", "0" );
|
|
||||||
+ encodeDataPair( data, "players", Integer.toString( ProxyServer.getInstance().getOnlineCount() ) );
|
|
||||||
+ encodeDataPair( data, "revision", String.valueOf( REVISION ) );
|
|
||||||
+
|
|
||||||
+ // If we're pinging, append it
|
|
||||||
+ if ( isPing )
|
|
||||||
+ {
|
|
||||||
+ encodeDataPair( data, "ping", "true" );
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ // Create the url
|
|
||||||
+ URL url = new URL( BASE_URL + String.format( REPORT_URL, encode( "Waterfall" ) ) );
|
+ URL url = new URL( BASE_URL + String.format( REPORT_URL, encode( "Waterfall" ) ) );
|
||||||
+
|
|
||||||
+ // Connect to the website
|
// Connect to the website
|
||||||
+ URLConnection connection;
|
URLConnection connection;
|
||||||
+
|
|
||||||
+ connection = url.openConnection();
|
|
||||||
+
|
|
||||||
+ connection.setDoOutput( true );
|
|
||||||
+ final BufferedReader reader;
|
|
||||||
+ final String response;
|
|
||||||
+ try ( OutputStreamWriter writer = new OutputStreamWriter( connection.getOutputStream() ) )
|
|
||||||
+ {
|
|
||||||
+ writer.write( data.toString() );
|
|
||||||
+ writer.flush();
|
|
||||||
+ reader = new BufferedReader( new InputStreamReader( connection.getInputStream() ) );
|
|
||||||
+ response = reader.readLine();
|
|
||||||
+ }
|
|
||||||
+ reader.close();
|
|
||||||
+
|
|
||||||
+ if ( response == null || response.startsWith( "ERR" ) )
|
|
||||||
+ {
|
|
||||||
+ throw new IOException( response ); //Throw the exception
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ /**
|
|
||||||
+ * <p>
|
|
||||||
+ * Encode a key/value data pair to be used in a HTTP post request. This
|
|
||||||
+ * INCLUDES a & so the first key/value pair MUST be included manually,
|
|
||||||
+ * e.g:</p>
|
|
||||||
+ * <code>
|
|
||||||
+ * StringBuffer data = new StringBuffer();
|
|
||||||
+ * data.append(encode("guid")).append('=').append(encode(guid));
|
|
||||||
+ * encodeDataPair(data, "version", description.getVersion());
|
|
||||||
+ * </code>
|
|
||||||
+ *
|
|
||||||
+ * @param buffer the StringBuilder to append the data pair onto
|
|
||||||
+ * @param key the key value
|
|
||||||
+ * @param value the value
|
|
||||||
+ */
|
|
||||||
+ private static void encodeDataPair(final StringBuilder buffer, final String key, final String value) throws UnsupportedEncodingException
|
|
||||||
+ {
|
|
||||||
+ buffer.append( '&' ).append( encode( key ) ).append( '=' ).append( encode( value ) );
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ /**
|
|
||||||
+ * Encode text as UTF-8
|
|
||||||
+ *
|
|
||||||
+ * @param text the text to encode
|
|
||||||
+ * @return the encoded text, as UTF-8
|
|
||||||
+ */
|
|
||||||
+ private static String encode(final String text) throws UnsupportedEncodingException
|
|
||||||
+ {
|
|
||||||
+ return URLEncoder.encode( text, "UTF-8" );
|
|
||||||
+ }
|
|
||||||
+}
|
|
||||||
diff --git a/proxy/src/main/java/io/github/waterfallmc/waterfall/conf/WaterfallConfiguration.java b/proxy/src/main/java/io/github/waterfallmc/waterfall/conf/WaterfallConfiguration.java
|
diff --git a/proxy/src/main/java/io/github/waterfallmc/waterfall/conf/WaterfallConfiguration.java b/proxy/src/main/java/io/github/waterfallmc/waterfall/conf/WaterfallConfiguration.java
|
||||||
index 12c5859..692b83e 100644
|
index 12c5859..692b83e 100644
|
||||||
--- a/proxy/src/main/java/io/github/waterfallmc/waterfall/conf/WaterfallConfiguration.java
|
--- a/proxy/src/main/java/io/github/waterfallmc/waterfall/conf/WaterfallConfiguration.java
|
||||||
@ -206,141 +109,6 @@ index 7bb0862..304a794 100644
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void startListeners()
|
public void startListeners()
|
||||||
diff --git a/proxy/src/main/java/net/md_5/bungee/Metrics.java b/proxy/src/main/java/net/md_5/bungee/Metrics.java
|
|
||||||
deleted file mode 100644
|
|
||||||
index 9523987..0000000
|
|
||||||
--- a/proxy/src/main/java/net/md_5/bungee/Metrics.java
|
|
||||||
+++ /dev/null
|
|
||||||
@@ -1,129 +0,0 @@
|
|
||||||
-package net.md_5.bungee;
|
|
||||||
-
|
|
||||||
-import java.io.BufferedReader;
|
|
||||||
-import java.io.IOException;
|
|
||||||
-import java.io.InputStreamReader;
|
|
||||||
-import java.io.OutputStreamWriter;
|
|
||||||
-import java.io.UnsupportedEncodingException;
|
|
||||||
-import java.net.URL;
|
|
||||||
-import java.net.URLConnection;
|
|
||||||
-import java.net.URLEncoder;
|
|
||||||
-import java.util.TimerTask;
|
|
||||||
-import net.md_5.bungee.api.ProxyServer;
|
|
||||||
-
|
|
||||||
-public class Metrics extends TimerTask
|
|
||||||
-{
|
|
||||||
-
|
|
||||||
- /**
|
|
||||||
- * The current revision number
|
|
||||||
- */
|
|
||||||
- private final static int REVISION = 5;
|
|
||||||
- /**
|
|
||||||
- * The base url of the metrics domain
|
|
||||||
- */
|
|
||||||
- private static final String BASE_URL = "http://mcstats.org";
|
|
||||||
- /**
|
|
||||||
- * The url used to report a server's status
|
|
||||||
- */
|
|
||||||
- private static final String REPORT_URL = "/report/%s";
|
|
||||||
- /**
|
|
||||||
- * Interval of time to ping (in minutes)
|
|
||||||
- */
|
|
||||||
- final static int PING_INTERVAL = 10;
|
|
||||||
- boolean firstPost = true;
|
|
||||||
-
|
|
||||||
- @Override
|
|
||||||
- public void run()
|
|
||||||
- {
|
|
||||||
- try
|
|
||||||
- {
|
|
||||||
- // We use the inverse of firstPost because if it is the first time we are posting,
|
|
||||||
- // it is not a interval ping, so it evaluates to FALSE
|
|
||||||
- // Each time thereafter it will evaluate to TRUE, i.e PING!
|
|
||||||
- postPlugin( !firstPost );
|
|
||||||
-
|
|
||||||
- // After the first post we set firstPost to false
|
|
||||||
- // Each post thereafter will be a ping
|
|
||||||
- firstPost = false;
|
|
||||||
- } catch ( IOException ex )
|
|
||||||
- {
|
|
||||||
- // ProxyServer.getInstance().getLogger().info( "[Metrics] " + ex.getMessage() );
|
|
||||||
- }
|
|
||||||
- }
|
|
||||||
-
|
|
||||||
- /**
|
|
||||||
- * Generic method that posts a plugin to the metrics website
|
|
||||||
- */
|
|
||||||
- private void postPlugin(boolean isPing) throws IOException
|
|
||||||
- {
|
|
||||||
- // Construct the post data
|
|
||||||
- final StringBuilder data = new StringBuilder();
|
|
||||||
- data.append( encode( "guid" ) ).append( '=' ).append( encode( BungeeCord.getInstance().config.getUuid() ) );
|
|
||||||
- encodeDataPair( data, "version", ProxyServer.getInstance().getVersion() );
|
|
||||||
- encodeDataPair( data, "server", "0" );
|
|
||||||
- encodeDataPair( data, "players", Integer.toString( ProxyServer.getInstance().getOnlineCount() ) );
|
|
||||||
- encodeDataPair( data, "revision", String.valueOf( REVISION ) );
|
|
||||||
-
|
|
||||||
- // If we're pinging, append it
|
|
||||||
- if ( isPing )
|
|
||||||
- {
|
|
||||||
- encodeDataPair( data, "ping", "true" );
|
|
||||||
- }
|
|
||||||
-
|
|
||||||
- // Create the url
|
|
||||||
- URL url = new URL( BASE_URL + String.format( REPORT_URL, encode( "BungeeCord" ) ) );
|
|
||||||
-
|
|
||||||
- // Connect to the website
|
|
||||||
- URLConnection connection;
|
|
||||||
-
|
|
||||||
- connection = url.openConnection();
|
|
||||||
-
|
|
||||||
- connection.setDoOutput( true );
|
|
||||||
- final BufferedReader reader;
|
|
||||||
- final String response;
|
|
||||||
- try ( OutputStreamWriter writer = new OutputStreamWriter( connection.getOutputStream() ) )
|
|
||||||
- {
|
|
||||||
- writer.write( data.toString() );
|
|
||||||
- writer.flush();
|
|
||||||
- reader = new BufferedReader( new InputStreamReader( connection.getInputStream() ) );
|
|
||||||
- response = reader.readLine();
|
|
||||||
- }
|
|
||||||
- reader.close();
|
|
||||||
-
|
|
||||||
- if ( response == null || response.startsWith( "ERR" ) )
|
|
||||||
- {
|
|
||||||
- throw new IOException( response ); //Throw the exception
|
|
||||||
- }
|
|
||||||
- }
|
|
||||||
-
|
|
||||||
- /**
|
|
||||||
- * <p>
|
|
||||||
- * Encode a key/value data pair to be used in a HTTP post request. This
|
|
||||||
- * INCLUDES a & so the first key/value pair MUST be included manually,
|
|
||||||
- * e.g:</p>
|
|
||||||
- * <code>
|
|
||||||
- * StringBuffer data = new StringBuffer();
|
|
||||||
- * data.append(encode("guid")).append('=').append(encode(guid));
|
|
||||||
- * encodeDataPair(data, "version", description.getVersion());
|
|
||||||
- * </code>
|
|
||||||
- *
|
|
||||||
- * @param buffer the StringBuilder to append the data pair onto
|
|
||||||
- * @param key the key value
|
|
||||||
- * @param value the value
|
|
||||||
- */
|
|
||||||
- private static void encodeDataPair(final StringBuilder buffer, final String key, final String value) throws UnsupportedEncodingException
|
|
||||||
- {
|
|
||||||
- buffer.append( '&' ).append( encode( key ) ).append( '=' ).append( encode( value ) );
|
|
||||||
- }
|
|
||||||
-
|
|
||||||
- /**
|
|
||||||
- * Encode text as UTF-8
|
|
||||||
- *
|
|
||||||
- * @param text the text to encode
|
|
||||||
- * @return the encoded text, as UTF-8
|
|
||||||
- */
|
|
||||||
- private static String encode(final String text) throws UnsupportedEncodingException
|
|
||||||
- {
|
|
||||||
- return URLEncoder.encode( text, "UTF-8" );
|
|
||||||
- }
|
|
||||||
-}
|
|
||||||
--
|
--
|
||||||
2.7.4 (Apple Git-66)
|
2.9.2
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
From 775f2851f8552cf918192df5ec3516083d3cc4e3 Mon Sep 17 00:00:00 2001
|
From 6228b5e5fbfe99d6c929f063c851ec948e543355 Mon Sep 17 00:00:00 2001
|
||||||
From: Techcable <Techcable@techcable.net>
|
From: Techcable <Techcable@techcable.net>
|
||||||
Date: Mon, 25 Apr 2016 23:46:00 -0700
|
Date: Mon, 25 Apr 2016 23:46:00 -0700
|
||||||
Subject: [PATCH] Reduce the overhead of lots and lots of teams with the same
|
Subject: [PATCH] Reduce the overhead of lots and lots of teams with the same
|
||||||
@ -11,10 +11,10 @@ Uses a sorted array to avoid the overhead of the hashset in a team.
|
|||||||
|
|
||||||
diff --git a/api/src/main/java/io/github/waterfallmc/waterfall/utils/LowMemorySet.java b/api/src/main/java/io/github/waterfallmc/waterfall/utils/LowMemorySet.java
|
diff --git a/api/src/main/java/io/github/waterfallmc/waterfall/utils/LowMemorySet.java b/api/src/main/java/io/github/waterfallmc/waterfall/utils/LowMemorySet.java
|
||||||
new file mode 100644
|
new file mode 100644
|
||||||
index 0000000..3a062b6
|
index 0000000..c62e3d4
|
||||||
--- /dev/null
|
--- /dev/null
|
||||||
+++ b/api/src/main/java/io/github/waterfallmc/waterfall/utils/LowMemorySet.java
|
+++ b/api/src/main/java/io/github/waterfallmc/waterfall/utils/LowMemorySet.java
|
||||||
@@ -0,0 +1,177 @@
|
@@ -0,0 +1,176 @@
|
||||||
+package io.github.waterfallmc.waterfall.utils;
|
+package io.github.waterfallmc.waterfall.utils;
|
||||||
+
|
+
|
||||||
+import lombok.*;
|
+import lombok.*;
|
||||||
@ -35,7 +35,7 @@ index 0000000..3a062b6
|
|||||||
+/**
|
+/**
|
||||||
+ * A set that uses a <a href=>binary search</a> to find objects in a <a href=https://en.wikipedia.org/wiki/Sorted_array>sorted array</a>.
|
+ * A set that uses a <a href=>binary search</a> to find objects in a <a href=https://en.wikipedia.org/wiki/Sorted_array>sorted array</a>.
|
||||||
+ * Avoids the memory cost of {@link java.util.HashSet}, while maintaining reasonable {@link Set#contains}
|
+ * Avoids the memory cost of {@link java.util.HashSet}, while maintaining reasonable {@link Set#contains}
|
||||||
+ * <b>Insertions ma O(N)!</b>
|
+ * <b>Insertions are O(N)!</b>
|
||||||
+ */
|
+ */
|
||||||
+public class LowMemorySet<T extends Comparable<T>> extends AbstractSet<T> implements Set<T> {
|
+public class LowMemorySet<T extends Comparable<T>> extends AbstractSet<T> implements Set<T> {
|
||||||
+ private final List<T> backing;
|
+ private final List<T> backing;
|
||||||
@ -56,7 +56,7 @@ index 0000000..3a062b6
|
|||||||
+ return new LowMemorySet<>(new ArrayList<>(c));
|
+ return new LowMemorySet<>(new ArrayList<>(c));
|
||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
+ @SuppressWarnings("unchecked") // nope
|
+ @SuppressWarnings({"unchecked", "rawtypes"})
|
||||||
+ private int indexOf(Object o) {
|
+ private int indexOf(Object o) {
|
||||||
+ return Collections.binarySearch((List) backing, o);
|
+ return Collections.binarySearch((List) backing, o);
|
||||||
+ }
|
+ }
|
||||||
@ -71,7 +71,7 @@ index 0000000..3a062b6
|
|||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
+ private void trim(boolean force) {
|
+ private void trim(boolean force) {
|
||||||
+ if (backing instanceof ArrayList && force || trimAggressively) ((ArrayList) backing).trimToSize();
|
+ if (backing instanceof ArrayList && force || trimAggressively) ((ArrayList<T>) backing).trimToSize();
|
||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
+ @Override
|
+ @Override
|
||||||
@ -88,8 +88,6 @@ index 0000000..3a062b6
|
|||||||
+ public Iterator<T> iterator() {
|
+ public Iterator<T> iterator() {
|
||||||
+ Iterator<T> backing = this.backing.iterator();
|
+ Iterator<T> backing = this.backing.iterator();
|
||||||
+ return new Iterator<T>() {
|
+ return new Iterator<T>() {
|
||||||
+ private T last;
|
|
||||||
+
|
|
||||||
+ @Override
|
+ @Override
|
||||||
+ public boolean hasNext() {
|
+ public boolean hasNext() {
|
||||||
+ return backing.hasNext();
|
+ return backing.hasNext();
|
||||||
@ -97,12 +95,12 @@ index 0000000..3a062b6
|
|||||||
+
|
+
|
||||||
+ @Override
|
+ @Override
|
||||||
+ public T next() {
|
+ public T next() {
|
||||||
+ return (last = backing.next());
|
+ return backing.next();
|
||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
+ @Override
|
+ @Override
|
||||||
+ public void remove() {
|
+ public void remove() {
|
||||||
+ LowMemorySet.this.remove(last);
|
+ backing.remove();
|
||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
+ @Override
|
+ @Override
|
||||||
@ -132,7 +130,9 @@ index 0000000..3a062b6
|
|||||||
+
|
+
|
||||||
+ @Override
|
+ @Override
|
||||||
+ public boolean remove(Object o) {
|
+ public boolean remove(Object o) {
|
||||||
+ T old = backing.remove(indexOf(o));
|
+ int index = indexOf(o);
|
||||||
|
+ if (index < 0) return false;
|
||||||
|
+ T old = backing.remove(index);
|
||||||
+ this.trim();
|
+ this.trim();
|
||||||
+ assert old == o;
|
+ assert old == o;
|
||||||
+ return old != null;
|
+ return old != null;
|
||||||
@ -155,7 +155,6 @@ index 0000000..3a062b6
|
|||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
+ @Override
|
+ @Override
|
||||||
+ @SuppressWarnings("unchecked")
|
|
||||||
+ public boolean addAll(Collection<? extends T> c) {
|
+ public boolean addAll(Collection<? extends T> c) {
|
||||||
+ if (containsAll(c)) return false;
|
+ if (containsAll(c)) return false;
|
||||||
+ backing.addAll(c);
|
+ backing.addAll(c);
|
||||||
@ -293,5 +292,5 @@ index 0000000..5aa306a
|
|||||||
+
|
+
|
||||||
+}
|
+}
|
||||||
--
|
--
|
||||||
2.7.4 (Apple Git-66)
|
2.9.2
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user