<commit_before>package space.pxls;

import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.JsonNode;
import com.mashape.unirest.http.Unirest;
import com.mashape.unirest.http.exceptions.UnirestException;
import org.eclipse.jetty.util.MultiMap;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
import org.json.JSONObject;
import org.slf4j.MarkerFactory;
import spark.Request;

import java.io.IOException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.regex.Pattern;

@WebSocket
public class WebSocketHandler {
    private Gson gson = new Gson();

    private Set<Session> sessions = ConcurrentHashMap.newKeySet();

    private long lastUserCountSent;
    private Queue<QueuedPixel> pixelQueue = new ConcurrentLinkedQueue<>();

    @OnWebSocketConnect
    public void connected(Session session) throws IOException {
        String ip = getIP(session);


        Profile sess = getSessionData(session);
        sess.mustFillOutCaptcha = true;

        if (!checkBanned(session)) {
            sendWaitTime(session);
        }

        sessions.add(session);

        int sessionLimit = App.getGame().getConfig().getInt("server.sessionLimit");
        if (sessionLimit > 0 && sess.role.ordinal() <= Role.DEFAULT.ordinal()) {
            int ss = 0;
            for (Session session1 : sessions) {
                if (getIP(session1).equalsIgnoreCase(ip)) {
                    ss++;
                }
            }
            if (ss > sessionLimit) {
                send(session, new Data.ServerSessionLimit());
                session.close();
                return;
            }
        }

        send(session, new Data.ServerUsers(sessions.size()));
        updateUserCount();
    }

    private void updateUserCount() {
        if (lastUserCountSent + 5000 > System.currentTimeMillis()) return;
        lastUserCountSent = System.currentTimeMillis();

        broadcast(new Data.ServerUsers(sessions.size()));
    }

    private boolean checkBanned(Session session) {
        String ip = getIP(session);
        if (getSessionData(session).role == Role.BANNED) {
            App.getLogger().info("Banned IP {} tried to connect", ip);
            send(session, new Data.ServerAlert("Due to abuse, this IP address has been banned from placing pixels"));
            return true;
        } else if (App.shouldBanTor() && App.getTorIps().contains(ip)) {
            App.getLogger().info("Tor IP {} tried to connect", ip);
            send(session, new Data.ServerAlert("Due to widespread abuse, Tor IP addresses have been banned from placing pixels"));
            return true;
        } else {
            String hostname = session.getRemoteAddress().getHostName();

            boolean blacklisted = false;
            for (String s : App.getGame().getConfig().getStringList("server.blacklistedHosts")) {
                Pattern p = Pattern.compile(s);
                if (p.matcher(hostname).find()) {
                    blacklisted = true;
                    break;
                }
            }

            if (blacklisted) {
                App.getLogger().info("Hostname {} ({}) matches blacklist, blocking", hostname, ip);
                send(session, new Data.ServerAlert("Due to widespread abuse, this IP address has been banned from placing pixels"));
                return true;
            }
        }
        return false;
    }

    @OnWebSocketMessage
    public void message(Session session, String message) throws IOException, UnirestException {
        JsonObject rawObject = gson.fromJson(message, JsonObject.class);
        if (!rawObject.has("type")) return;

        String type = rawObject.get("type").getAsString();

        if (type.equals("placepixel")) {
            if (checkBanned(session)) return;

            Data.ClientPlace cp = gson.fromJson(rawObject, Data.ClientPlace.class);
            doPlace(session, cp);
        } else if (type.equals("captcha")) {
            Data.ClientCaptcha cc = gson.fromJson(rawObject, Data.ClientCaptcha.class);
            doCaptcha(session, cc);
        } else if (type.equals("place")) {
            Data.ClientPlace cp = gson.fromJson(rawObject, Data.ClientPlace.class);
            App.getLogger().info("SUSPICIOUS: {} is possible bot, tried to place {} at {},{}", getIP(session), cp.color, cp.x, cp.y);
        } else if (type.equals("admin_cdoverride")) {
            Profile p = getSessionData(session);
            if (p.role.ordinal() < Role.TRUSTED.ordinal()) {
                App.getLogger().info("IP {} tried to override cooldown, no perms", getIP(session));
                return;
            }

            Data.ClientSetCooldownOverride csco = gson.fromJson(rawObject, Data.ClientSetCooldownOverride.class);
            p.overrideCooldown = csco.override;
            sendWaitTime(session);
        }
    }

    private void doCaptcha(Session session, Data.ClientCaptcha cc) throws UnirestException {
        Profile data = getSessionData(session);
        if (!data.mustFillOutCaptcha) return;

        boolean success = verifyCaptcha(session, cc.token);
        if (success) {
            data.mustFillOutCaptcha = false;
            data.justCaptchaed = true;
        }
        send(session, new Data.ServerCaptchaStatus(success));
    }

    private void doPlace(Session session, Data.ClientPlace cp) throws IOException {
        String ip = getIP(session);
        Profile data = getSessionData(session);

        int x = cp.x;
        int y = cp.y;
        int color = cp.color;

        if (x < 0 || x >= App.getGame().getWidth() || y < 0 || y >= App.getGame().getHeight() || color < 0 || color >= App.getGame().getPalette().size())
            return;

        boolean trusted = data.role.ordinal() >= Role.TRUSTED.ordinal();

        float waitTime = App.getGame().getWaitTime(data.lastPlace);

        if (!data.justCaptchaed) updateCaptchaState(data, trusted);

        if (waitTime <= 0 || data.overrideCooldown) {
            if (data.mustFillOutCaptcha) {
                App.getLogger().debug("IP {} must fill out captcha, rejecting place request", ip);
                send(session, new Data.ServerCaptchaNeeded());
                return;
            } else {
                if (App.getGame().getPixel(x, y) == color) return;
                App.getGame().setPixel(x, y, (byte) color, ip);

                int queue = App.getGame().getConfig().getInt("server.queuePixels");

                if (queue > 0) {
                    pixelQueue.add(new QueuedPixel(x, y, color));
                    if (pixelQueue.size() >= queue) {
                        Data.ServerPlace sp = new Data.ServerPlace(pixelQueue);
                        broadcast(sp);
                        pixelQueue.clear();
                    }
                } else {
                    Data.ServerPlace sp = new Data.ServerPlace(Collections.singleton(new QueuedPixel(x, y, color)));
                    broadcast(sp);
                }

                data.lastPlace = System.currentTimeMillis();

                if (data.overrideCooldown) {
                    App.getLogger().debug("IP {} overrode cooldown and placed pixel", ip);
                }

                data.justCaptchaed = false;
            }
        }

        sendWaitTime(session);
    }

    private void updateCaptchaState(Profile data, boolean trusted) {<commit_msg>Multithreading sucks<commit_after>package space.pxls;

import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.JsonNode;
import com.mashape.unirest.http.Unirest;
import com.mashape.unirest.http.exceptions.UnirestException;
import org.eclipse.jetty.util.MultiMap;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
import org.json.JSONObject;
import org.slf4j.MarkerFactory;
import spark.Request;

import java.io.IOException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.regex.Pattern;

@WebSocket
public class WebSocketHandler {
    private Gson gson = new Gson();

    private Set<Session> sessions = ConcurrentHashMap.newKeySet();

    private long lastUserCountSent;
    private Queue<QueuedPixel> pixelQueue = new ConcurrentLinkedQueue<>();

    @OnWebSocketConnect
    public void connected(Session session) throws IOException {
        String ip = getIP(session);


        Profile sess = getSessionData(session);
        sess.mustFillOutCaptcha = true;

        if (!checkBanned(session)) {
            sendWaitTime(session);
        }

        sessions.add(session);

        int sessionLimit = App.getGame().getConfig().getInt("server.sessionLimit");
        if (sessionLimit > 0 && sess.role.ordinal() <= Role.DEFAULT.ordinal()) {
            int ss = 0;
            for (Session session1 : sessions) {
                if (getIP(session1).equalsIgnoreCase(ip)) {
                    ss++;
                }
            }
            if (ss > sessionLimit) {
                send(session, new Data.ServerSessionLimit());
                session.close();
                return;
            }
        }

        send(session, new Data.ServerUsers(sessions.size()));
        updateUserCount();
    }

    private void updateUserCount() {
        if (lastUserCountSent + 5000 > System.currentTimeMillis()) return;
        lastUserCountSent = System.currentTimeMillis();

        broadcast(new Data.ServerUsers(sessions.size()));
    }

    private boolean checkBanned(Session session) {
        String ip = getIP(session);
        if (getSessionData(session).role == Role.BANNED) {
            App.getLogger().info("Banned IP {} tried to connect", ip);
            send(session, new Data.ServerAlert("Due to abuse, this IP address has been banned from placing pixels"));
            return true;
        } else if (App.shouldBanTor() && App.getTorIps().contains(ip)) {
            App.getLogger().info("Tor IP {} tried to connect", ip);
            send(session, new Data.ServerAlert("Due to widespread abuse, Tor IP addresses have been banned from placing pixels"));
            return true;
        } else {
            String hostname = session.getRemoteAddress().getHostName();

            boolean blacklisted = false;
            for (String s : App.getGame().getConfig().getStringList("server.blacklistedHosts")) {
                Pattern p = Pattern.compile(s);
                if (p.matcher(hostname).find()) {
                    blacklisted = true;
                    break;
                }
            }

            if (blacklisted) {
                App.getLogger().info("Hostname {} ({}) matches blacklist, blocking", hostname, ip);
                send(session, new Data.ServerAlert("Due to widespread abuse, this IP address has been banned from placing pixels"));
                return true;
            }
        }
        return false;
    }

    @OnWebSocketMessage
    public void message(Session session, String message) throws IOException, UnirestException {
        JsonObject rawObject = gson.fromJson(message, JsonObject.class);
        if (!rawObject.has("type")) return;

        String type = rawObject.get("type").getAsString();

        if (type.equals("placepixel")) {
            if (checkBanned(session)) return;

            Data.ClientPlace cp = gson.fromJson(rawObject, Data.ClientPlace.class);
            doPlace(session, cp);
        } else if (type.equals("captcha")) {
            Data.ClientCaptcha cc = gson.fromJson(rawObject, Data.ClientCaptcha.class);
            doCaptcha(session, cc);
        } else if (type.equals("place")) {
            Data.ClientPlace cp = gson.fromJson(rawObject, Data.ClientPlace.class);
            App.getLogger().info("SUSPICIOUS: {} is possible bot, tried to place {} at {},{}", getIP(session), cp.color, cp.x, cp.y);
        } else if (type.equals("admin_cdoverride")) {
            Profile p = getSessionData(session);
            if (p.role.ordinal() < Role.TRUSTED.ordinal()) {
                App.getLogger().info("IP {} tried to override cooldown, no perms", getIP(session));
                return;
            }

            Data.ClientSetCooldownOverride csco = gson.fromJson(rawObject, Data.ClientSetCooldownOverride.class);
            p.overrideCooldown = csco.override;
            sendWaitTime(session);
        }
    }

    private void doCaptcha(Session session, Data.ClientCaptcha cc) throws UnirestException {
        Profile data = getSessionData(session);
        if (!data.mustFillOutCaptcha) return;

        boolean success = verifyCaptcha(session, cc.token);
        if (success) {
            data.mustFillOutCaptcha = false;
            data.justCaptchaed = true;
        }
        send(session, new Data.ServerCaptchaStatus(success));
    }

    private void doPlace(Session session, Data.ClientPlace cp) throws IOException {
        String ip = getIP(session);
        Profile data = getSessionData(session);

        int x = cp.x;
        int y = cp.y;
        int color = cp.color;

        if (x < 0 || x >= App.getGame().getWidth() || y < 0 || y >= App.getGame().getHeight() || color < 0 || color >= App.getGame().getPalette().size())
            return;

        boolean trusted = data.role.ordinal() >= Role.TRUSTED.ordinal();

        float waitTime = App.getGame().getWaitTime(data.lastPlace);

        if (!data.justCaptchaed) updateCaptchaState(data, trusted);

        if (waitTime <= 0 || data.overrideCooldown) {
            if (data.mustFillOutCaptcha) {
                App.getLogger().debug("IP {} must fill out captcha, rejecting place request", ip);
                send(session, new Data.ServerCaptchaNeeded());
                return;
            } else {
                if (App.getGame().getPixel(x, y) == color) return;
                App.getGame().setPixel(x, y, (byte) color, ip);

                int queueAmount = App.getGame().getConfig().getInt("server.queuePixels");

                if (queueAmount > 0) {
                    pixelQueue.add(new QueuedPixel(x, y, color));

                    if (pixelQueue.size() >= queueAmount) {
                        Data.ServerPlace sp = new Data.ServerPlace(new ArrayList<>(pixelQueue));
                        pixelQueue.clear();
                        broadcast(sp);
                    }
                } else {
                    Data.ServerPlace sp = new Data.ServerPlace(Collections.singleton(new QueuedPixel(x, y, color)));
                    broadcast(sp);
                }

                data.lastPlace = System.currentTimeMillis();

                if (data.overrideCooldown) {
                    App.getLogger().debug("IP {} overrode cooldown and placed pixel", ip);
                }

                data.justCaptchaed = false;
            }
        }

        sendWaitTime(session);
    }

    private void updateCaptchaState(Profile data, boolean trusted) {
{'fraction_non_alphanumeric': 0.1153527499840673, 'fraction_numerical': 0.0017844624306927537, 'mean_word_length': 2.2529021558872304, 'pattern_counts': {'":': 0, '<': 22, '<?xml version=': 0, '>': 28, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3425978', 'n_tokens_mistral': 4384, 'n_tokens_neox': 4210, 'n_words': 938}