parent
fcf86712f7
commit
2fd334472e
@ -71,14 +71,14 @@ public class Leaderboard
|
||||
return _type;
|
||||
}
|
||||
|
||||
public void update(Map<String, Long> names)
|
||||
public void update(Map<String, Integer> names)
|
||||
{
|
||||
deconstruct();
|
||||
LinkedList<String> display = new LinkedList<>();
|
||||
display.add(C.cAqua + _display);
|
||||
display.add(C.cRed + " ");
|
||||
int place = _start + 1;
|
||||
for (Entry<String, Long> entry : names.entrySet())
|
||||
for (Entry<String, Integer> entry : names.entrySet())
|
||||
{
|
||||
if (entry.getValue() == 1)
|
||||
{
|
||||
|
@ -127,11 +127,11 @@ public class LeaderboardRepository extends RepositoryBase
|
||||
});
|
||||
}
|
||||
|
||||
public void loadLeaderboard(Leaderboard board, Consumer<Map<String, Long>> leaderboard)
|
||||
public void loadLeaderboard(Leaderboard board, Consumer<Map<String, Integer>> leaderboard)
|
||||
{
|
||||
UtilServer.runAsync(() ->
|
||||
{
|
||||
Map<String, Long> names = new LinkedHashMap<>();
|
||||
Map<String, Integer> names = new LinkedHashMap<>();
|
||||
try (
|
||||
Connection c = getConnection();
|
||||
Statement s = c.createStatement();
|
||||
@ -144,7 +144,7 @@ public class LeaderboardRepository extends RepositoryBase
|
||||
{
|
||||
while (rs.next())
|
||||
{
|
||||
names.merge(rs.getString("name"), rs.getLong("value"), Long::sum);
|
||||
names.merge(rs.getString("name"), rs.getInt("value"), Integer::sum);
|
||||
}
|
||||
|
||||
if (!s.getMoreResults())
|
||||
@ -166,11 +166,11 @@ public class LeaderboardRepository extends RepositoryBase
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void loadLeaderboards(Collection<Leaderboard> boards, Consumer<Map<String, Long>[]> leaderboard)
|
||||
public void loadLeaderboards(Collection<Leaderboard> boards, Consumer<Map<String, Integer>[]> leaderboard)
|
||||
{
|
||||
UtilServer.runAsync(() ->
|
||||
{
|
||||
Map<String, Long>[] leaderboards = new Map[boards.size()];
|
||||
Map<String, Integer>[] leaderboards = new Map[boards.size()];
|
||||
StringBuilder queryBuilder = new StringBuilder();
|
||||
{
|
||||
int i = 0;
|
||||
@ -199,7 +199,7 @@ public class LeaderboardRepository extends RepositoryBase
|
||||
{
|
||||
while (rs.next())
|
||||
{
|
||||
leaderboards[index].merge(rs.getString("name"), rs.getLong("value"), Long::sum);
|
||||
leaderboards[index].merge(rs.getString("name"), rs.getInt("value"), Integer::sum);
|
||||
}
|
||||
|
||||
if (!s.getMoreResults())
|
||||
|
@ -5,7 +5,6 @@ import java.sql.SQLException;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.Future;
|
||||
@ -22,12 +21,12 @@ import mineplex.core.account.CoreClient;
|
||||
import mineplex.core.account.CoreClientManager;
|
||||
import mineplex.core.account.permissions.Permission;
|
||||
import mineplex.core.account.permissions.PermissionGroup;
|
||||
import mineplex.core.common.util.F;
|
||||
import mineplex.core.common.util.UtilPlayer;
|
||||
import mineplex.core.common.util.UtilServer;
|
||||
import mineplex.core.common.util.UtilTasks;
|
||||
import mineplex.core.leaderboard.LeaderboardManager;
|
||||
import mineplex.core.stats.command.GiveStatCommand;
|
||||
import mineplex.core.stats.command.MasterBuilderUnban;
|
||||
import mineplex.core.stats.command.SetLevelCommand;
|
||||
import mineplex.core.stats.command.TimeCommand;
|
||||
import mineplex.core.stats.event.StatChangeEvent;
|
||||
import mineplex.core.thread.ThreadPool;
|
||||
@ -70,12 +69,12 @@ public class StatsManager extends MiniDbClientPlugin<PlayerStats>
|
||||
{
|
||||
save(_statUploadQueue, map ->
|
||||
{
|
||||
//_repository.saveStats(map);
|
||||
_repository.saveStats(map);
|
||||
_leaderboard.handleStatIncrease(map);
|
||||
}, "increment");
|
||||
save(_statUploadQueueOverRidable, map ->
|
||||
{
|
||||
//_repository.saveStats(map, true);
|
||||
_repository.saveStats(map, true);
|
||||
_leaderboard.handleStatIncrease(map);
|
||||
}, "override");
|
||||
});
|
||||
@ -90,6 +89,7 @@ public class StatsManager extends MiniDbClientPlugin<PlayerStats>
|
||||
|
||||
private void generatePermissions()
|
||||
{
|
||||
|
||||
PermissionGroup.ADMIN.setPermission(Perm.GIVE_STAT_COMMAND, true, true);
|
||||
PermissionGroup.MOD.setPermission(Perm.TIME_COMMAND, true, true);
|
||||
PermissionGroup.ADMIN.setPermission(Perm.SET_LEVEL_COMMAND, true, true);
|
||||
@ -297,12 +297,9 @@ public class StatsManager extends MiniDbClientPlugin<PlayerStats>
|
||||
if (callback != null) callback.run();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_repository.registerNewStat(statName, () ->
|
||||
{
|
||||
synchronized(STATS_LOCK)
|
||||
{
|
||||
_repository.registerNewStat(statName);
|
||||
|
||||
_stats.clear();
|
||||
|
||||
for (Stat stat : _repository.retrieveStats())
|
||||
@ -313,7 +310,6 @@ public class StatsManager extends MiniDbClientPlugin<PlayerStats>
|
||||
if (callback != null) callback.run();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
@ -336,43 +332,6 @@ public class StatsManager extends MiniDbClientPlugin<PlayerStats>
|
||||
{
|
||||
setStat(event.getPlayer(), stat, 0);
|
||||
});
|
||||
|
||||
final UUID uuid = event.getPlayer().getUniqueId();
|
||||
final int accountId = _coreClientManager.getAccountId(event.getPlayer());
|
||||
UtilPlayer.message(event.getPlayer(), F.main(getName(), "We are currently converting to a different stat storage method, please wait a moment if your stats are not all present!"));
|
||||
runAsync(() ->
|
||||
{
|
||||
_repository.loadStatsFromOld(accountId, statMap ->
|
||||
{
|
||||
if (Bukkit.getPlayer(uuid) == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
boolean conversionOccurred = false;
|
||||
Map<String, Long> currentMap = Get(event.getPlayer()).getStats();
|
||||
for (Entry<String, Long> oldEntry : statMap.entrySet())
|
||||
{
|
||||
if (currentMap.containsKey(oldEntry.getKey()))
|
||||
{
|
||||
if (currentMap.get(oldEntry.getKey()) < oldEntry.getValue())
|
||||
{
|
||||
conversionOccurred = true;
|
||||
Long diff = oldEntry.getValue() - currentMap.get(oldEntry.getKey());
|
||||
incrementStat(event.getPlayer(), oldEntry.getKey(), diff);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
conversionOccurred = true;
|
||||
incrementStat(event.getPlayer(), oldEntry.getKey(), oldEntry.getValue());
|
||||
}
|
||||
}
|
||||
if (conversionOccurred)
|
||||
{
|
||||
UtilPlayer.message(event.getPlayer(), F.main(getName(), "Your old stats have been converted!"));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -380,8 +339,8 @@ public class StatsManager extends MiniDbClientPlugin<PlayerStats>
|
||||
{
|
||||
addCommand(new TimeCommand(this));
|
||||
addCommand(new GiveStatCommand(this));
|
||||
//addCommand(new SetLevelCommand(this));
|
||||
//addCommand(new MasterBuilderUnban(this));
|
||||
addCommand(new SetLevelCommand(this));
|
||||
addCommand(new MasterBuilderUnban(this));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -414,6 +373,6 @@ public class StatsManager extends MiniDbClientPlugin<PlayerStats>
|
||||
@Override
|
||||
public String getQuery(int accountId, String uuid, String name)
|
||||
{
|
||||
return "SELECT stats.name, value FROM accountStatsAllTime INNER JOIN stats ON stats.id = accountStatsAllTime.statId WHERE accountStatsAllTime.accountId = '" + accountId + "';";
|
||||
return "SELECT stats.name, value FROM accountStat INNER JOIN stats ON stats.id = accountStat.statId WHERE accountStat.accountId = '" + accountId + "';";
|
||||
}
|
||||
}
|
@ -1,14 +1,10 @@
|
||||
package mineplex.core.stats;
|
||||
|
||||
import java.sql.CallableStatement;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.jooq.DSLContext;
|
||||
import org.jooq.Insert;
|
||||
@ -19,11 +15,10 @@ import org.jooq.Update;
|
||||
import org.jooq.impl.DSL;
|
||||
import org.jooq.types.ULong;
|
||||
|
||||
import mineplex.core.common.util.UtilServer;
|
||||
import mineplex.core.database.MinecraftRepository;
|
||||
import mineplex.database.Tables;
|
||||
import mineplex.serverdata.database.DBPool;
|
||||
import mineplex.serverdata.database.RepositoryBase;
|
||||
import mineplex.serverdata.database.column.ColumnInt;
|
||||
import mineplex.serverdata.database.column.ColumnVarChar;
|
||||
|
||||
public class StatsRepository extends RepositoryBase
|
||||
@ -36,20 +31,6 @@ public class StatsRepository extends RepositoryBase
|
||||
super(DBPool.getAccount());
|
||||
}
|
||||
|
||||
public void loadStatsFromOld(int accountId, Consumer<Map<String, Long>> callback)
|
||||
{
|
||||
executeQuery("SELECT stats.name, value FROM accountStat INNER JOIN stats ON stats.id = accountStat.statId WHERE accountStat.accountId=?;", rs ->
|
||||
{
|
||||
final Map<String, Long> statMap = new HashMap<>();
|
||||
while (rs.next())
|
||||
{
|
||||
statMap.put(rs.getString(1), rs.getLong(2));
|
||||
}
|
||||
|
||||
UtilServer.runSync(() -> callback.accept(statMap));
|
||||
}, new ColumnInt("accountId", accountId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all the remote registered stats
|
||||
*
|
||||
@ -75,28 +56,9 @@ public class StatsRepository extends RepositoryBase
|
||||
*
|
||||
* @param name The name of the stat
|
||||
*/
|
||||
public void registerNewStat(String name, Runnable onComplete)
|
||||
public void registerNewStat(String name)
|
||||
{
|
||||
try (Connection c = getConnection())
|
||||
{
|
||||
executeInsert(c, "INSERT INTO stats (name) VALUES (?);", rs ->
|
||||
{
|
||||
if (rs.next())
|
||||
{
|
||||
final int id = rs.getInt(1);
|
||||
try (CallableStatement cs = c.prepareCall("{call accountStatsCreateBucketData(?)}"))
|
||||
{
|
||||
cs.setInt("stat", id);
|
||||
cs.execute();
|
||||
onComplete.run();
|
||||
}
|
||||
}
|
||||
}, () -> {}, new ColumnVarChar("name", 100, name));
|
||||
}
|
||||
catch (SQLException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
executeUpdate(INSERT_STAT, new ColumnVarChar("name", 100, name));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,62 +0,0 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>StatConverter</groupId>
|
||||
<artifactId>StatConverter</artifactId>
|
||||
<version>1.0</version>
|
||||
<build>
|
||||
<sourceDirectory>src</sourceDirectory>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src</directory>
|
||||
<excludes>
|
||||
<exclude>**/*.java</exclude>
|
||||
</excludes>
|
||||
</resource>
|
||||
</resources>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.1</version>
|
||||
<configuration>
|
||||
<source>1.8</source>
|
||||
<target>1.8</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>2.3</version>
|
||||
<configuration>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>2.4.3</version>
|
||||
<type>maven-plugin</type>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-dbcp2</artifactId>
|
||||
<version>2.0.1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<version>5.1.14</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
@ -1,168 +0,0 @@
|
||||
package com.mineplex.statconverter;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.sql.Connection;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import com.google.common.io.Files;
|
||||
import com.mineplex.statconverter.database.mysql.DBPool;
|
||||
|
||||
public class Main
|
||||
{
|
||||
public static void main(String[] args)
|
||||
{
|
||||
new Main();
|
||||
}
|
||||
|
||||
private File _info;
|
||||
|
||||
public Main()
|
||||
{
|
||||
int start = 0;
|
||||
try
|
||||
{
|
||||
_info = new File(new File(".").getCanonicalPath() + File.separator + "converterInfo.dat");
|
||||
System.out.println(_info.getCanonicalPath());
|
||||
if (_info.exists())
|
||||
{
|
||||
String startStr = Files.readFirstLine(_info, Charset.defaultCharset());
|
||||
if (startStr != null && !startStr.isEmpty())
|
||||
{
|
||||
start = Integer.parseInt(startStr);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_info.createNewFile();
|
||||
Files.write("0".getBytes(), _info);
|
||||
}
|
||||
|
||||
convertGroup(start);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void convertGroup(int start)
|
||||
{
|
||||
System.out.println("[INFO] Starting " + start + " to " + (start + 9999));
|
||||
List<Integer> accounts = new ArrayList<>();
|
||||
try (Connection c = DBPool.getDataSource("ACCOUNT_PC").getConnection())
|
||||
{
|
||||
try (Statement s = c.createStatement();
|
||||
ResultSet rs = s.executeQuery("SELECT DISTINCT accountId FROM accountStat LIMIT " + start + ",10000;")
|
||||
)
|
||||
{
|
||||
while (rs.next())
|
||||
{
|
||||
accounts.add(rs.getInt("accountId"));
|
||||
}
|
||||
}
|
||||
|
||||
if (!accounts.isEmpty())
|
||||
{
|
||||
for (Integer accountId : accounts)
|
||||
{
|
||||
Map<Integer, Long> oldStats = new HashMap<>();
|
||||
Map<Integer, Long> newStats = new HashMap<>();
|
||||
try (Statement s = c.createStatement();
|
||||
ResultSet rs = s.executeQuery("SELECT statId, value FROM accountStat WHERE accountId=" + accountId + ";")
|
||||
)
|
||||
{
|
||||
while (rs.next())
|
||||
{
|
||||
oldStats.put(rs.getInt("statId"), rs.getLong("value"));
|
||||
}
|
||||
}
|
||||
try (Statement s = c.createStatement();
|
||||
ResultSet rs = s.executeQuery("SELECT statId, value FROM accountStatsAllTime WHERE accountId=" + accountId + ";")
|
||||
)
|
||||
{
|
||||
while (rs.next())
|
||||
{
|
||||
newStats.put(rs.getInt("statId"), rs.getLong("value"));
|
||||
}
|
||||
}
|
||||
|
||||
for (Entry<Integer, Long> oldEntry : oldStats.entrySet())
|
||||
{
|
||||
if (newStats.containsKey(oldEntry.getKey()))
|
||||
{
|
||||
if (newStats.get(oldEntry.getKey()) < oldEntry.getValue())
|
||||
{
|
||||
try (Statement s = c.createStatement())
|
||||
{
|
||||
Long diff = oldEntry.getValue() - newStats.get(oldEntry.getKey());
|
||||
s.execute("UPDATE accountStatsAllTime SET value=value+" + diff + " WHERE accountId=" + accountId + " AND statId=" + oldEntry.getKey() + ";");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
try (Statement s = c.createStatement())
|
||||
{
|
||||
s.execute("INSERT INTO accountStatsAllTime (accountId, statId, value) VALUES (" + accountId + ", " + oldEntry.getKey() + ", " + oldEntry.getValue() + ");");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try (Statement s = c.createStatement())
|
||||
{
|
||||
s.execute("DELETE FROM accountStat WHERE accountId=" + accountId + ";");
|
||||
}
|
||||
}
|
||||
complete(start);
|
||||
}
|
||||
else
|
||||
{
|
||||
System.out.println("[INFO] Conversion complete");
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
{
|
||||
Thread.sleep(3000);
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (SQLException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void complete(int start)
|
||||
{
|
||||
int nextStart = start + 10000;
|
||||
if (_info.delete())
|
||||
{
|
||||
try
|
||||
{
|
||||
_info.createNewFile();
|
||||
Files.write((nextStart + "").getBytes(), _info);
|
||||
System.out.println("[INFO] Completed " + start + " to " + (nextStart - 1));
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,90 +0,0 @@
|
||||
package com.mineplex.statconverter.database.mysql;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.Files;
|
||||
import java.sql.Connection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.apache.commons.dbcp2.BasicDataSource;
|
||||
|
||||
public final class DBPool
|
||||
{
|
||||
private static final Map<String, DataSource> _dataSources = new ConcurrentHashMap<>();
|
||||
|
||||
public static DataSource getDataSource(String databaseId)
|
||||
{
|
||||
if (_dataSources == null || _dataSources.isEmpty())
|
||||
{
|
||||
loadDataSources();
|
||||
}
|
||||
return _dataSources.get(databaseId);
|
||||
}
|
||||
|
||||
private static DataSource openDataSource(String url, String username, String password)
|
||||
{
|
||||
BasicDataSource source = new BasicDataSource();
|
||||
source.addConnectionProperty("autoReconnect", "true");
|
||||
source.addConnectionProperty("allowMultiQueries", "true");
|
||||
source.setDefaultTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
|
||||
source.setDriverClassName("com.mysql.jdbc.Driver");
|
||||
source.setUrl(url);
|
||||
source.setUsername(username);
|
||||
source.setPassword(password);
|
||||
source.setMaxTotal(3);
|
||||
source.setMaxIdle(3);
|
||||
source.setTimeBetweenEvictionRunsMillis(180 * 1000);
|
||||
source.setSoftMinEvictableIdleTimeMillis(180 * 1000);
|
||||
|
||||
return source;
|
||||
}
|
||||
|
||||
private static void loadDataSources()
|
||||
{
|
||||
Map<String, DataSource> dataSources = new HashMap<>();
|
||||
try
|
||||
{
|
||||
File configFile = new File(new File(".").getCanonicalPath() + File.separator + "database-config.dat");
|
||||
|
||||
if (configFile.exists())
|
||||
{
|
||||
List<String> lines = Files.readAllLines(configFile.toPath(),
|
||||
Charset.defaultCharset());
|
||||
|
||||
for (String line : lines)
|
||||
{
|
||||
String[] args = line.split(" ");
|
||||
|
||||
if (args.length == 4)
|
||||
{
|
||||
String dbId = args[0];
|
||||
String dbHost = args[1];
|
||||
String userName = args[2];
|
||||
String password = args[3];
|
||||
|
||||
dataSources.put(dbId, openDataSource("jdbc:mysql://" + dbHost, userName, password));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
System.out.println("database-config.dat not found at "
|
||||
+ configFile.toPath().toString());
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
exception.printStackTrace();
|
||||
System.out.println("---Unable To Parse DBPOOL Configuration File---");
|
||||
}
|
||||
|
||||
_dataSources.replaceAll((database, source) -> dataSources.getOrDefault(database, source));
|
||||
dataSources.forEach((database, source) -> _dataSources.putIfAbsent(database, source));
|
||||
_dataSources.entrySet().removeIf(entry -> !dataSources.containsKey(entry.getKey()));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user