<commit_before>package rlbot.cppinterop;

import com.google.flatbuffers.FlatBufferBuilder;
import com.sun.jna.Memory;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import rlbot.ControllerState;
import rlbot.flat.*;
import rlbot.gamestate.GameStatePacket;
import rlbot.render.RenderPacket;

import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;

/**
 * This is the main communication gateway between Java and Rocket League.
 * It contains methods for retrieving data, sending car controls, rendering graphics, etc.
 *
 * It does so via the "interface dll" which is vended in the rlbot python package at
 * https://pypi.org/project/rlbot/ and authored at https://github.com/RLBot/RLBot
 */
public class RLBotDll {

    // Java bots must either add this in their classpath, or specify the jna.library.path JVM arg.
    private static final String LOCAL_DLL_DIR = "build/dll";
    private static final String DLL_NAME_SANS_EXTENSION = "RLBot_Core_Interface";
    private static final String DLL_NAME_SANS_EXTENSION_32 = "RLBot_Core_Interface_32";

    private static native ByteBufferStruct UpdateLiveDataPacketFlatbuffer();
    private static native int UpdatePlayerInputFlatbuffer(Pointer ptr, int size);
    private static native ByteBufferStruct UpdateFieldInfoFlatbuffer();
    private static native int RenderGroup(Pointer ptr, int size);
    private static native int SetGameState(Pointer ptr, int size);
    private static native ByteBufferStruct GetBallPrediction();
    private static native int SendQuickChat(Pointer ptr, int size);
    private static native int StartMatchFlatbuffer(Pointer ptr, int size);
    private static native boolean IsInitialized();  // This asks the dll instance whether it is done initializing.

    // This helps us keep track internally of whether we have initialized, so that we only do it once.
    private static boolean isInitializationComplete = false;
    private static final Object fileLock = new Object();

    public static void initialize(final String interfaceDllPath) throws IOException {
        synchronized(fileLock) {
            if (isInitializationComplete) {
                return;
            }

            // https://www.quora.com/How-do-you-know-if-youre-running-Java-in-32-bits-or-64-bits
            boolean is64Bit = System.getProperty("os.arch").contains("64");
            System.out.println("Detected " + (is64Bit ? "64" : "32") + "bit JVM.");

            final String dllNameSansExtension = is64Bit ? DLL_NAME_SANS_EXTENSION : DLL_NAME_SANS_EXTENSION_32;

            final File directory = getDllDirectory();

            if (!directory.exists()) {
                directory.mkdirs();
            }

            String dllName = dllNameSansExtension + ".dll";

            // Currently interfaceDllPath points to a 64 or 32 bit dll based on the bitness
            // of the python installation. That's not what we want, we care about the
            // bitness of the JVM, so pick the correct one from the folder.
            Path dllSource = Paths.get(interfaceDllPath).getParent().resolve(dllName);

            // Copy the interface dll to a known location that is already on the classpath.
            Files.copy(
                    dllSource,
                    Paths.get(directory.toString(), dllName),
                    StandardCopyOption.REPLACE_EXISTING);

            System.out.println("Loading DLL from " + dllSource);
            Native.register(dllNameSansExtension);

            int attemptCount = 0;
            while (!IsInitialized()) {
                try {
                    attemptCount++;
                    if (attemptCount > 100) {
                        // Should throw after about 10 seconds of waiting.
                        throw new IOException("The RLBot interface appears to be taking forever to initialize!");
                    }
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            isInitializationComplete = true;
        }
    }

    private static File getDllDirectory() {
        final String jnaPath = System.getProperty("jna.library.path");
        if (jnaPath == null) {
            return new File(LOCAL_DLL_DIR);
        }
        return new File(jnaPath);
    }

    /**
     * Retrieves up-to-date game information like the positions of the ball and cars, among many other things.
     */
    public static GameTickPacket getFlatbufferPacket() throws IOException {
        try {
            final ByteBufferStruct struct = UpdateLiveDataPacketFlatbuffer();
            if (struct.size < 4) {
                throw new IOException("Flatbuffer packet is too small, match is probably not running!");
            }
            final byte[] protoBytes = struct.ptr.getByteArray(0, struct.size);
            return GameTickPacket.getRootAsGameTickPacket(ByteBuffer.wrap(protoBytes));
        } catch (final UnsatisfiedLinkError error) {
            throw new IOException("Could not find interface dll! Did initialize get called?", error);
        } catch (final Error error) {
            throw new IOException(error);
        }
    }

    /**
     * Retrieves information about boost pad locations, goal locations, dropshot tile locations, etc.
     */
    public static FieldInfo getFieldInfo() throws IOException {
        try {
            final ByteBufferStruct struct = UpdateFieldInfoFlatbuffer();<commit_msg>Using canonical paths when setting up the dll directory for java.<commit_after>package rlbot.cppinterop;

import com.google.flatbuffers.FlatBufferBuilder;
import com.sun.jna.Memory;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import rlbot.ControllerState;
import rlbot.flat.*;
import rlbot.gamestate.GameStatePacket;
import rlbot.render.RenderPacket;

import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;

/**
 * This is the main communication gateway between Java and Rocket League.
 * It contains methods for retrieving data, sending car controls, rendering graphics, etc.
 *
 * It does so via the "interface dll" which is vended in the rlbot python package at
 * https://pypi.org/project/rlbot/ and authored at https://github.com/RLBot/RLBot
 */
public class RLBotDll {

    // Java bots must either add this in their classpath, or specify the jna.library.path JVM arg.
    private static final String LOCAL_DLL_DIR = "build/dll";
    private static final String DLL_NAME_SANS_EXTENSION = "RLBot_Core_Interface";
    private static final String DLL_NAME_SANS_EXTENSION_32 = "RLBot_Core_Interface_32";

    private static native ByteBufferStruct UpdateLiveDataPacketFlatbuffer();
    private static native int UpdatePlayerInputFlatbuffer(Pointer ptr, int size);
    private static native ByteBufferStruct UpdateFieldInfoFlatbuffer();
    private static native int RenderGroup(Pointer ptr, int size);
    private static native int SetGameState(Pointer ptr, int size);
    private static native ByteBufferStruct GetBallPrediction();
    private static native int SendQuickChat(Pointer ptr, int size);
    private static native int StartMatchFlatbuffer(Pointer ptr, int size);
    private static native boolean IsInitialized();  // This asks the dll instance whether it is done initializing.

    // This helps us keep track internally of whether we have initialized, so that we only do it once.
    private static boolean isInitializationComplete = false;
    private static final Object fileLock = new Object();

    public static void initialize(final String interfaceDllPath) throws IOException {
        synchronized(fileLock) {
            if (isInitializationComplete) {
                return;
            }

            // https://www.quora.com/How-do-you-know-if-youre-running-Java-in-32-bits-or-64-bits
            boolean is64Bit = System.getProperty("os.arch").contains("64");
            System.out.println("Detected " + (is64Bit ? "64" : "32") + "bit JVM.");

            final String dllNameSansExtension = is64Bit ? DLL_NAME_SANS_EXTENSION : DLL_NAME_SANS_EXTENSION_32;

            final File directory = getDllDirectory();

            if (!directory.exists()) {
                directory.mkdirs();
            }

            String dllName = dllNameSansExtension + ".dll";

            // Currently interfaceDllPath points to a 64 or 32 bit dll based on the bitness
            // of the python installation. That's not what we want, we care about the
            // bitness of the JVM, so pick the correct one from the folder.
            Path dllSource = Paths.get(interfaceDllPath).getParent().resolve(dllName);

            // Copy the interface dll to a known location that is already on the classpath.
            Files.copy(
                    dllSource,
                    Paths.get(directory.toString(), dllName),
                    StandardCopyOption.REPLACE_EXISTING);

            System.out.println("Loading DLL from " + dllSource);
            Native.register(dllNameSansExtension);

            int attemptCount = 0;
            while (!IsInitialized()) {
                try {
                    attemptCount++;
                    if (attemptCount > 100) {
                        // Should throw after about 10 seconds of waiting.
                        throw new IOException("The RLBot interface appears to be taking forever to initialize!");
                    }
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            isInitializationComplete = true;
        }
    }

    private static File getDllDirectory() throws IOException {
        final String jnaPath = System.getProperty("jna.library.path");
        if (jnaPath == null) {
            return new File(LOCAL_DLL_DIR).getCanonicalFile();
        }
        return new File(jnaPath).getCanonicalFile();
    }

    /**
     * Retrieves up-to-date game information like the positions of the ball and cars, among many other things.
     */
    public static GameTickPacket getFlatbufferPacket() throws IOException {
        try {
            final ByteBufferStruct struct = UpdateLiveDataPacketFlatbuffer();
            if (struct.size < 4) {
                throw new IOException("Flatbuffer packet is too small, match is probably not running!");
            }
            final byte[] protoBytes = struct.ptr.getByteArray(0, struct.size);
            return GameTickPacket.getRootAsGameTickPacket(ByteBuffer.wrap(protoBytes));
        } catch (final UnsatisfiedLinkError error) {
            throw new IOException("Could not find interface dll! Did initialize get called?", error);
        } catch (final Error error) {
            throw new IOException(error);
        }
    }

    /**
     * Retrieves information about boost pad locations, goal locations, dropshot tile locations, etc.
     */
    public static FieldInfo getFieldInfo() throws IOException {
        try {
            final ByteBufferStruct struct = UpdateFieldInfoFlatbuffer();
{'fraction_non_alphanumeric': 0.08012340237990304, 'fraction_numerical': 0.006522697223446452, 'mean_word_length': 2.5345794392523366, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 6, 'lorem ipsum': 0, 'www.': 2, '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': '1360058', 'n_tokens_mistral': 3028, 'n_tokens_neox': 2867, 'n_words': 943}