> ## Documentation Index
> Fetch the complete documentation index at: https://simplecloud.app/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Installation

> Add the Cloud API to your Minecraft plugin

export const DependencySnippet = ({dependencies = [], repositories = [{
  id: "simplecloud",
  url: "https://repo.simplecloud.app/snapshots"
}, {
  id: "buf",
  url: "https://buf.build/gen/maven"
}], language = "en", type = "snapshot"}) => {
  const [versions, setVersions] = useState({});
  const [failed, setFailed] = useState({});
  const dependencyKey = dependencies.map(dep => `${dep.groupId}:${dep.artifactId}:${dep.version || ""}`).join("|");
  const copy = {
    en: {
      loading: "Loading the latest dependency version from the Maven repository...",
      failed: "The latest version could not be loaded. The examples below use dynamic version selectors that resolve the newest available artifact during dependency resolution."
    },
    de: {
      loading: "Die neueste Dependency-Version wird aus dem Maven-Repository geladen...",
      failed: "Die neueste Version konnte nicht geladen werden. Die Beispiele unten verwenden dynamische Versionsselektoren, die beim Auflösen der Dependency das neueste verfügbare Artefakt verwenden."
    }
  };
  const text = copy[language] || copy.en;
  useEffect(() => {
    let cancelled = false;
    dependencies.forEach(dep => {
      if (dep.version) return;
      const key = `${dep.groupId}:${dep.artifactId}`;
      const fetchVersion = async () => {
        try {
          const url = type === "snapshot" ? `https://repo.simplecloud.app/api/maven/latest/version/snapshots/${dep.groupId.replace(/\./g, "/")}/${dep.artifactId}?type=raw` : `https://search.maven.org/solrsearch/select?q=g:${dep.groupId}+AND+a:${dep.artifactId}&rows=1&wt=json`;
          const response = await fetch(url, {
            cache: "no-store"
          });
          if (!response.ok) throw new Error(`Version request failed: ${response.status}`);
          if (type === "snapshot") {
            const version = (await response.text()).trim();
            if (!cancelled && version) {
              setVersions(current => ({
                ...current,
                [key]: version
              }));
            }
            return;
          }
          const data = await response.json();
          const version = data.response?.docs?.[0]?.latestVersion;
          if (!cancelled && version) {
            setVersions(current => ({
              ...current,
              [key]: version
            }));
          }
        } catch (error) {
          if (!cancelled) {
            setFailed(current => ({
              ...current,
              [key]: true
            }));
          }
        }
      };
      fetchVersion();
    });
    return () => {
      cancelled = true;
    };
  }, [dependencyKey, type]);
  const keyFor = dep => `${dep.groupId}:${dep.artifactId}`;
  const hasPendingVersion = dependencies.some(dep => !dep.version && !versions[keyFor(dep)] && !failed[keyFor(dep)]);
  const hasFailedVersion = dependencies.some(dep => failed[keyFor(dep)]);
  const getVersion = (dep, buildTool) => {
    if (dep.version) return dep.version;
    const version = versions[keyFor(dep)];
    if (version) return version;
    return buildTool === "maven" ? "[0,)" : "latest.integration";
  };
  const generateKotlin = () => {
    const repoLines = repositories.map(repo => `    maven("${repo.url}")`).join("\n");
    const depLines = dependencies.map(dep => {
      const depType = dep.type || "implementation";
      return `    ${depType}("${dep.groupId}:${dep.artifactId}:${getVersion(dep, "gradle")}")`;
    }).join("\n");
    return `repositories {\n${repoLines}\n}\n\ndependencies {\n${depLines}\n}`;
  };
  const generateGroovy = () => {
    const repoLines = repositories.map(repo => `    maven { url '${repo.url}' }`).join("\n");
    const depLines = dependencies.map(dep => {
      const depType = dep.type || "implementation";
      return `    ${depType} '${dep.groupId}:${dep.artifactId}:${getVersion(dep, "gradle")}'`;
    }).join("\n");
    return `repositories {\n${repoLines}\n}\n\ndependencies {\n${depLines}\n}`;
  };
  const generateMaven = () => {
    const repoLines = repositories.map(repo => `    <repository>
      <id>${repo.id || "repo"}</id>
      <url>${repo.url}</url>
    </repository>`).join("\n");
    const depLines = dependencies.map(dep => {
      const scope = dep.type === "compileOnly" ? "provided" : "compile";
      return `    <dependency>
      <groupId>${dep.groupId}</groupId>
      <artifactId>${dep.artifactId}</artifactId>
      <version>${getVersion(dep, "maven")}</version>
      <scope>${scope}</scope>
    </dependency>`;
    }).join("\n");
    return `<repositories>\n${repoLines}\n</repositories>\n\n<dependencies>\n${depLines}\n</dependencies>`;
  };
  if (hasPendingVersion) {
    return <Info>{text.loading}</Info>;
  }
  return <>
      {hasFailedVersion && <Warning>
          {text.failed}
        </Warning>}

      <Tabs>
        <Tab title="Gradle (Kotlin)">
          <CodeBlock language="kotlin">{generateKotlin()}</CodeBlock>
        </Tab>
        <Tab title="Gradle (Groovy)">
          <CodeBlock language="groovy">{generateGroovy()}</CodeBlock>
        </Tab>
        <Tab title="Maven">
          <CodeBlock language="xml">{generateMaven()}</CodeBlock>
        </Tab>
      </Tabs>
    </>;
};

## Add the API dependency

The Cloud API requires Java 21. Add the SimpleCloud and Buf repositories, then add the API with `compileOnly` in Gradle or `provided` in Maven:

<DependencySnippet
  dependencies={[
{
  groupId: "app.simplecloud.api",
  artifactId: "api",
  type: "compileOnly",
},
]}
/>

<Warning>
  Do not shade the Cloud API into your plugin. The `simplecloud-api` platform plugin provides it at runtime, and bundling another copy can cause class-loading conflicts.
</Warning>

Pin the dependency to the version you tested before releasing your plugin.

## Require the platform plugin

Declare `simplecloud-api` as a required dependency so it loads before your plugin. SimpleCloud's default templates already include the matching platform plugin. If you use custom templates, keep it in the `plugins` directory.

<Tabs>
  <Tab title="Paper">
    Add the dependency to `src/main/resources/paper-plugin.yml`:

    ```yaml paper-plugin.yml theme={null}
    name: my-plugin
    version: "1.0.0"
    main: com.example.MyPlugin
    api-version: "1.21"
    dependencies:
      server:
        simplecloud-api:
          load: BEFORE
          required: true
          join-classpath: true
    ```
  </Tab>

  <Tab title="Folia">
    Add the dependency to `src/main/resources/plugin.yml`:

    ```yaml plugin.yml theme={null}
    name: my-plugin
    version: "1.0.0"
    main: com.example.MyPlugin
    api-version: "1.21"
    folia-supported: true
    depend:
      - simplecloud-api
    ```
  </Tab>

  <Tab title="Spigot">
    Add the dependency to `src/main/resources/plugin.yml`:

    ```yaml plugin.yml theme={null}
    name: my-plugin
    version: "1.0.0"
    main: com.example.MyPlugin
    api-version: "1.20"
    depend:
      - simplecloud-api
    ```
  </Tab>

  <Tab title="Velocity">
    Add the dependency to the `@Plugin` annotation on your main class:

    ```java MyPlugin.java theme={null}
    import com.velocitypowered.api.plugin.Dependency;
    import com.velocitypowered.api.plugin.Plugin;

    @Plugin(
        id = "my-plugin",
        name = "My Plugin",
        version = "1.0.0",
        dependencies = {@Dependency(id = "simplecloud-api")}
    )
    public final class MyPlugin {
    }
    ```
  </Tab>

  <Tab title="BungeeCord">
    Add the dependency to `src/main/resources/plugin.yml`:

    ```yaml plugin.yml theme={null}
    name: my-plugin
    version: "1.0.0"
    main: com.example.MyPlugin
    depends:
      - simplecloud-api
    ```
  </Tab>
</Tabs>

## Create one client

Create a `CloudApi` instance when your plugin starts, reuse it for every request, and close it when your plugin stops. SimpleCloud provides the connection settings automatically.

```java MyPlugin.java theme={null}
package com.example;

import app.simplecloud.api.CloudApi;
import org.bukkit.plugin.java.JavaPlugin;

public final class MyPlugin extends JavaPlugin {
    private CloudApi cloudApi;

    @Override
    public void onEnable() {
        cloudApi = CloudApi.create();
    }

    @Override
    public void onDisable() {
        if (cloudApi != null) {
            cloudApi.close();
        }
    }
}
```

Most API calls return `CompletableFuture`; compose them instead of blocking, and use your platform's scheduler before changing platform state from a callback.

### Velocity lifecycle

On Velocity, create and close the same client from the proxy lifecycle events:

```java MyPlugin.java theme={null}
private CloudApi cloudApi;

@Subscribe
public void onProxyInitialize(ProxyInitializeEvent event) {
    cloudApi = CloudApi.create();
}

@Subscribe
public void onProxyShutdown(ProxyShutdownEvent event) {
    if (cloudApi != null) {
        cloudApi.close();
    }
}
```

<Note>
  `CloudApi.create()` uses the connection settings supplied by SimpleCloud. If your plugin needs different request timeouts or cache behavior, pass a custom `CloudApiOptions` instance.
</Note>

## Troubleshooting

<AccordionGroup>
  <Accordion title="The API dependency cannot be resolved">
    Confirm that your build contains both repositories shown above, then refresh dependencies with `./gradlew --refresh-dependencies` or `mvn -U package`.
  </Accordion>

  <Accordion title="The plugin fails with NoClassDefFoundError">
    Check that:

    * The matching `simplecloud-api` platform plugin is installed and enabled.
    * Your plugin declares it as a required dependency. Paper plugins also need `join-classpath: true`.
    * Your build uses `compileOnly` or `provided` and does not shade the API.
  </Accordion>

  <Accordion title="CloudApi.create() fails">
    Confirm that SimpleCloud started the server and that the matching `simplecloud-api` platform plugin is enabled.
  </Accordion>
</AccordionGroup>
