initial
This commit is contained in:
Generated
+5
@@ -0,0 +1,5 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Environment-dependent path to Maven home directory
|
||||
/mavenHomeManager.xml
|
||||
Generated
+13
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<annotationProcessing>
|
||||
<profile name="Maven default annotation processors profile" enabled="true">
|
||||
<sourceOutputDir name="target/generated-sources/annotations" />
|
||||
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
|
||||
<outputRelativeToContentRoot value="true" />
|
||||
<module name="CctvPlugin" />
|
||||
</profile>
|
||||
</annotationProcessing>
|
||||
</component>
|
||||
</project>
|
||||
Generated
+7
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Encoding">
|
||||
<file url="file://$PROJECT_DIR$/src/main/kotlin" charset="UTF-8" />
|
||||
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
|
||||
</component>
|
||||
</project>
|
||||
Generated
+30
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="RemoteRepositoriesConfiguration">
|
||||
<remote-repository>
|
||||
<option name="id" value="central" />
|
||||
<option name="name" value="Central Repository" />
|
||||
<option name="url" value="https://repo.maven.apache.org/maven2" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="sonatype" />
|
||||
<option name="name" value="sonatype" />
|
||||
<option name="url" value="https://oss.sonatype.org/content/groups/public/" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="papermc-repo" />
|
||||
<option name="name" value="papermc-repo" />
|
||||
<option name="url" value="https://repo.papermc.io/repository/maven-public/" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="central" />
|
||||
<option name="name" value="Maven Central repository" />
|
||||
<option name="url" value="https://repo1.maven.org/maven2" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="jboss.community" />
|
||||
<option name="name" value="JBoss Community repository" />
|
||||
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
|
||||
</remote-repository>
|
||||
</component>
|
||||
</project>
|
||||
Generated
+6
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="KotlinJpsPluginSettings">
|
||||
<option name="version" value="2.2.0-Beta1" />
|
||||
</component>
|
||||
</project>
|
||||
Generated
+12
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="MaterialThemeProjectNewConfig">
|
||||
<option name="metadata">
|
||||
<MTProjectMetadataState>
|
||||
<option name="migrated" value="true" />
|
||||
<option name="pristineConfig" value="false" />
|
||||
<option name="userId" value="-2e14201d:196619b083a:-7ffe" />
|
||||
</MTProjectMetadataState>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
Generated
+14
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="MavenProjectsManager">
|
||||
<option name="originalFiles">
|
||||
<list>
|
||||
<option value="$PROJECT_DIR$/pom.xml" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_24" default="true" project-jdk-name="24" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
</project>
|
||||
Generated
+8
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/CctvPlugin.iml" filepath="$PROJECT_DIR$/CctvPlugin.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
Generated
+6
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module version="4">
|
||||
<component name="FacetManager">
|
||||
<facet type="minecraft" name="Minecraft">
|
||||
<configuration>
|
||||
<autoDetectTypes>
|
||||
<platformType>PAPER</platformType>
|
||||
</autoDetectTypes>
|
||||
<projectReimportVersion>1</projectReimportVersion>
|
||||
</configuration>
|
||||
</facet>
|
||||
</component>
|
||||
</module>
|
||||
@@ -0,0 +1,72 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.CctvPlugin</groupId>
|
||||
<artifactId>CctvPlugin</artifactId>
|
||||
<name>CctvPlugin</name>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<build>
|
||||
<sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory>
|
||||
<defaultGoal>clean package</defaultGoal>
|
||||
<resources>
|
||||
<resource>
|
||||
<filtering>true</filtering>
|
||||
<directory>src/main/resources</directory>
|
||||
</resource>
|
||||
</resources>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-maven-plugin</artifactId>
|
||||
<version>${kotlin.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>compile</id>
|
||||
<phase>compile</phase>
|
||||
<goals>
|
||||
<goal>compile</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<jvmTarget>${java.version}</jvmTarget>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.5.3</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>papermc-repo</id>
|
||||
<url>https://repo.papermc.io/repository/maven-public/</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>sonatype</id>
|
||||
<url>https://oss.sonatype.org/content/groups/public/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.papermc.paper</groupId>
|
||||
<artifactId>paper-api</artifactId>
|
||||
<version>1.21.5-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<kotlin.version>2.2.0-Beta1</kotlin.version>
|
||||
<java.version>21</java.version>
|
||||
</properties>
|
||||
</project>
|
||||
@@ -0,0 +1,87 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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>org.CctvPlugin</groupId>
|
||||
<artifactId>CctvPlugin</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>CctvPlugin</name>
|
||||
|
||||
<properties>
|
||||
<java.version>21</java.version>
|
||||
<kotlin.version>2.2.0-Beta1</kotlin.version>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<defaultGoal>clean package</defaultGoal>
|
||||
<sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-maven-plugin</artifactId>
|
||||
<version>${kotlin.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>compile</id>
|
||||
<phase>compile</phase>
|
||||
<goals>
|
||||
<goal>compile</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<jvmTarget>${java.version}</jvmTarget>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.5.3</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<filtering>true</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
</build>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>papermc-repo</id>
|
||||
<url>https://repo.papermc.io/repository/maven-public/</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>sonatype</id>
|
||||
<url>https://oss.sonatype.org/content/groups/public/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.papermc.paper</groupId>
|
||||
<artifactId>paper-api</artifactId>
|
||||
<version>1.21.5-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-stdlib-jdk8</artifactId>
|
||||
<version>${kotlin.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,115 @@
|
||||
// CctvCommand.kt
|
||||
package com.example.cctv
|
||||
|
||||
import org.bukkit.command.Command
|
||||
import org.bukkit.command.CommandExecutor
|
||||
import org.bukkit.command.CommandSender
|
||||
import org.bukkit.command.TabCompleter
|
||||
import org.bukkit.entity.Player
|
||||
import com.example.cctv.menus.MainMenu
|
||||
import com.example.cctv.menus.ViewCamerasMenu
|
||||
import com.example.cctv.menus.ManageCamerasMenu
|
||||
import com.example.cctv.menus.PlaceCameraMenu
|
||||
|
||||
class CctvCommand(private val plugin: CctvPlugin) : CommandExecutor, TabCompleter {
|
||||
override fun onCommand(sender: CommandSender, command: Command, label: String, args: Array<out String>): Boolean {
|
||||
if (sender !is Player) {
|
||||
sender.sendMessage("§cThis command can only be used by players.")
|
||||
return true
|
||||
}
|
||||
|
||||
if (!sender.hasPermission("cctv.use")) {
|
||||
sender.sendMessage("§cYou don't have permission to use this command.")
|
||||
return true
|
||||
}
|
||||
|
||||
if (args.isEmpty()) {
|
||||
MainMenu(plugin, sender).open()
|
||||
return true
|
||||
}
|
||||
|
||||
when (args[0].lowercase()) {
|
||||
"place", "create", "add" -> {
|
||||
if (!sender.hasPermission("cctv.place")) {
|
||||
sender.sendMessage("§cYou don't have permission to place cameras.")
|
||||
return true
|
||||
}
|
||||
|
||||
if (args.size < 2) {
|
||||
sender.sendMessage("§cUsage: /cctv place <name>")
|
||||
return true
|
||||
}
|
||||
|
||||
val name = args.drop(1).joinToString(" ")
|
||||
if (plugin.createCamera(sender, name)) {
|
||||
sender.sendMessage("§aCamera '$name' placed successfully!")
|
||||
} else {
|
||||
sender.sendMessage("§cFailed to place camera.")
|
||||
}
|
||||
}
|
||||
|
||||
"list", "view" -> {
|
||||
ViewCamerasMenu(plugin, sender).open()
|
||||
}
|
||||
|
||||
"remove", "delete" -> {
|
||||
if (args.size < 2) {
|
||||
ManageCamerasMenu(plugin, sender).open()
|
||||
return true
|
||||
}
|
||||
|
||||
val id = args[1]
|
||||
if (plugin.removeCamera(id, sender)) {
|
||||
sender.sendMessage("§aCamera removed successfully!")
|
||||
} else {
|
||||
sender.sendMessage("§cFailed to remove camera. Make sure you own it or have admin permissions.")
|
||||
}
|
||||
}
|
||||
|
||||
"help" -> {
|
||||
sender.sendMessage("§6=== CCTV Plugin Help ===")
|
||||
sender.sendMessage("§e/cctv §7- Open main menu")
|
||||
sender.sendMessage("§e/cctv place <name> §7- Place a camera at your location")
|
||||
sender.sendMessage("§e/cctv list §7- View available cameras")
|
||||
sender.sendMessage("§e/cctv remove [id] §7- Remove a camera")
|
||||
sender.sendMessage("§e/cctv help §7- Show this help message")
|
||||
}
|
||||
|
||||
else -> {
|
||||
sender.sendMessage("§cUnknown subcommand. Use '/cctv help' for help.")
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onTabComplete(sender: CommandSender, command: Command, alias: String, args: Array<out String>): List<String> {
|
||||
if (sender !is Player) return emptyList()
|
||||
|
||||
if (args.isEmpty()) {
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
if (args.size == 1) {
|
||||
val subCommands = mutableListOf<String>()
|
||||
if (sender.hasPermission("cctv.use")) {
|
||||
subCommands.addAll(listOf("help", "list", "view"))
|
||||
}
|
||||
if (sender.hasPermission("cctv.place")) {
|
||||
subCommands.addAll(listOf("place", "create", "add"))
|
||||
}
|
||||
if (sender.hasPermission("cctv.remove") || sender.hasPermission("cctv.admin")) {
|
||||
subCommands.addAll(listOf("remove", "delete"))
|
||||
}
|
||||
|
||||
return subCommands.filter { it.startsWith(args[0].lowercase()) }
|
||||
}
|
||||
|
||||
if (args.size == 2 && (args[0].equals("remove", ignoreCase = true) || args[0].equals("delete", ignoreCase = true))) {
|
||||
val cameras = plugin.getCameras()
|
||||
return cameras.keys.filter { it.startsWith(args[1]) }
|
||||
}
|
||||
|
||||
return emptyList()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,235 @@
|
||||
// CctvPlugin.kt
|
||||
package com.example.cctv
|
||||
|
||||
import org.bukkit.Bukkit
|
||||
import org.bukkit.Location
|
||||
import org.bukkit.Material
|
||||
import org.bukkit.configuration.file.YamlConfiguration
|
||||
import org.bukkit.entity.Player
|
||||
import org.bukkit.event.EventHandler
|
||||
import org.bukkit.event.Listener
|
||||
import org.bukkit.event.inventory.InventoryClickEvent
|
||||
import org.bukkit.event.player.PlayerJoinEvent
|
||||
import org.bukkit.event.player.PlayerQuitEvent
|
||||
import org.bukkit.inventory.ItemStack
|
||||
import org.bukkit.plugin.java.JavaPlugin
|
||||
import org.bukkit.scheduler.BukkitTask
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
import com.example.cctv.menus.CctvMenu
|
||||
import com.example.cctv.menus.MainMenu
|
||||
import com.example.cctv.menus.ViewCamerasMenu
|
||||
import com.example.cctv.menus.ManageCamerasMenu
|
||||
import com.example.cctv.menus.PlaceCameraMenu
|
||||
|
||||
class CctvPlugin : JavaPlugin(), Listener {
|
||||
private val cameras = mutableMapOf<String, Camera>()
|
||||
private val playerViewing = mutableMapOf<UUID, Camera>()
|
||||
private val viewingTasks = mutableMapOf<UUID, BukkitTask>()
|
||||
private lateinit var cameraFile: File
|
||||
private lateinit var cameraConfig: YamlConfiguration
|
||||
|
||||
override fun onEnable() {
|
||||
// Create config if it doesn't exist
|
||||
saveDefaultConfig()
|
||||
|
||||
// Register command
|
||||
getCommand("cctv")?.setExecutor(CctvCommand(this))
|
||||
|
||||
// Register events
|
||||
server.pluginManager.registerEvents(this, this)
|
||||
|
||||
// Initialize camera data
|
||||
cameraFile = File(dataFolder, "cameras.yml")
|
||||
if (!cameraFile.exists()) {
|
||||
saveResource("cameras.yml", false)
|
||||
}
|
||||
cameraConfig = YamlConfiguration.loadConfiguration(cameraFile)
|
||||
|
||||
// Load cameras from configuration
|
||||
loadCameras()
|
||||
|
||||
logger.info("CCTV Plugin enabled!")
|
||||
}
|
||||
|
||||
override fun onDisable() {
|
||||
// Stop all viewing sessions
|
||||
playerViewing.keys.forEach { stopViewing(it) }
|
||||
|
||||
// Save cameras to configuration
|
||||
saveCameras()
|
||||
|
||||
logger.info("CCTV Plugin disabled!")
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
fun onPlayerQuit(event: PlayerQuitEvent) {
|
||||
// Stop viewing when player leaves
|
||||
stopViewing(event.player.uniqueId)
|
||||
}
|
||||
|
||||
private fun loadCameras() {
|
||||
if (cameraConfig.contains("cameras")) {
|
||||
val camerasSection = cameraConfig.getConfigurationSection("cameras")
|
||||
camerasSection?.getKeys(false)?.forEach { id ->
|
||||
val section = camerasSection.getConfigurationSection(id)
|
||||
if (section != null) {
|
||||
val world = Bukkit.getWorld(section.getString("world") ?: "world")
|
||||
if (world != null) {
|
||||
val x = section.getDouble("x")
|
||||
val y = section.getDouble("y")
|
||||
val z = section.getDouble("z")
|
||||
val yaw = section.getDouble("yaw").toFloat()
|
||||
val pitch = section.getDouble("pitch").toFloat()
|
||||
val name = section.getString("name") ?: "Camera $id"
|
||||
val owner = UUID.fromString(section.getString("owner") ?: "00000000-0000-0000-0000-000000000000")
|
||||
|
||||
val location = Location(world, x, y, z, yaw, pitch)
|
||||
cameras[id] = Camera(id, name, location, owner)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun saveCameras() {
|
||||
// Clear existing cameras
|
||||
cameraConfig.set("cameras", null)
|
||||
|
||||
// Save each camera
|
||||
cameras.forEach { (id, camera) ->
|
||||
val path = "cameras.$id"
|
||||
cameraConfig.set("$path.name", camera.name)
|
||||
cameraConfig.set("$path.world", camera.location.world?.name)
|
||||
cameraConfig.set("$path.x", camera.location.x)
|
||||
cameraConfig.set("$path.y", camera.location.y)
|
||||
cameraConfig.set("$path.z", camera.location.z)
|
||||
cameraConfig.set("$path.yaw", camera.location.yaw)
|
||||
cameraConfig.set("$path.pitch", camera.location.pitch)
|
||||
cameraConfig.set("$path.owner", camera.owner.toString())
|
||||
}
|
||||
|
||||
// Save to file
|
||||
cameraConfig.save(cameraFile)
|
||||
}
|
||||
|
||||
fun getCameras(): Map<String, Camera> {
|
||||
return cameras.toMap()
|
||||
}
|
||||
|
||||
fun createCamera(player: Player, name: String): Boolean {
|
||||
val id = UUID.randomUUID().toString().substring(0, 8)
|
||||
val camera = Camera(id, name, player.location.clone(), player.uniqueId)
|
||||
cameras[id] = camera
|
||||
saveCameras()
|
||||
return true
|
||||
}
|
||||
|
||||
fun removeCamera(id: String, player: Player): Boolean {
|
||||
val camera = cameras[id] ?: return false
|
||||
|
||||
// Check if the player is the owner or has permission
|
||||
if (camera.owner != player.uniqueId && !player.hasPermission("cctv.admin")) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Stop anyone viewing this camera
|
||||
playerViewing.forEach { (uuid, cam) ->
|
||||
if (cam.id == id) {
|
||||
stopViewing(uuid)
|
||||
}
|
||||
}
|
||||
|
||||
cameras.remove(id)
|
||||
saveCameras()
|
||||
return true
|
||||
}
|
||||
|
||||
fun startViewing(player: Player, cameraId: String): Boolean {
|
||||
// Stop current viewing if any
|
||||
stopViewing(player.uniqueId)
|
||||
|
||||
val camera = cameras[cameraId] ?: return false
|
||||
|
||||
// Store original location
|
||||
val originalLocation = player.location.clone()
|
||||
camera.viewerLocations[player.uniqueId] = originalLocation
|
||||
|
||||
// Teleport to camera view (spectator mode)
|
||||
val gameMode = player.gameMode
|
||||
camera.viewerGameModes[player.uniqueId] = gameMode
|
||||
|
||||
player.gameMode = org.bukkit.GameMode.SPECTATOR
|
||||
player.teleport(camera.location)
|
||||
|
||||
// Set up viewing task
|
||||
playerViewing[player.uniqueId] = camera
|
||||
|
||||
// Create a task to check if player tries to exit spectator
|
||||
val task = Bukkit.getScheduler().runTaskTimer(this, Runnable {
|
||||
val currentPlayer = Bukkit.getPlayer(player.uniqueId)
|
||||
if (currentPlayer == null || !currentPlayer.isOnline) {
|
||||
stopViewing(player.uniqueId)
|
||||
return@Runnable
|
||||
}
|
||||
|
||||
// Keep the player at the camera location
|
||||
if (currentPlayer.location.distance(camera.location) > 0.5) {
|
||||
currentPlayer.teleport(camera.location)
|
||||
}
|
||||
}, 5L, 5L)
|
||||
|
||||
viewingTasks[player.uniqueId] = task
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
fun stopViewing(uuid: UUID) {
|
||||
val camera = playerViewing[uuid] ?: return
|
||||
val player = Bukkit.getPlayer(uuid) ?: return
|
||||
|
||||
// Restore original location
|
||||
val originalLocation = camera.viewerLocations[uuid]
|
||||
if (originalLocation != null) {
|
||||
player.teleport(originalLocation)
|
||||
camera.viewerLocations.remove(uuid)
|
||||
}
|
||||
|
||||
// Restore game mode
|
||||
val originalGameMode = camera.viewerGameModes[uuid]
|
||||
if (originalGameMode != null) {
|
||||
player.gameMode = originalGameMode
|
||||
camera.viewerGameModes.remove(uuid)
|
||||
}
|
||||
|
||||
// Cancel task
|
||||
viewingTasks[uuid]?.cancel()
|
||||
viewingTasks.remove(uuid)
|
||||
|
||||
// Remove from viewing map
|
||||
playerViewing.remove(uuid)
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
fun onInventoryClick(event: InventoryClickEvent) {
|
||||
val player = event.whoClicked as? Player ?: return
|
||||
val inventory = event.inventory
|
||||
|
||||
if (inventory.holder is CctvMenu) {
|
||||
event.isCancelled = true
|
||||
val item = event.currentItem ?: return
|
||||
|
||||
val menu = inventory.holder as CctvMenu
|
||||
menu.handleClick(player, item, event.slot)
|
||||
}
|
||||
}
|
||||
|
||||
data class Camera(
|
||||
val id: String,
|
||||
val name: String,
|
||||
val location: Location,
|
||||
val owner: UUID,
|
||||
val viewerLocations: MutableMap<UUID, Location> = mutableMapOf(),
|
||||
val viewerGameModes: MutableMap<UUID, org.bukkit.GameMode> = mutableMapOf()
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,288 @@
|
||||
// Menus.kt
|
||||
package com.example.cctv.menus
|
||||
|
||||
import org.bukkit.Bukkit
|
||||
import org.bukkit.Material
|
||||
import org.bukkit.entity.Player
|
||||
import org.bukkit.inventory.Inventory
|
||||
import org.bukkit.inventory.InventoryHolder
|
||||
import org.bukkit.inventory.ItemStack
|
||||
import org.bukkit.inventory.meta.ItemMeta
|
||||
import java.util.*
|
||||
import com.example.cctv.CctvPlugin
|
||||
|
||||
interface CctvMenu : InventoryHolder {
|
||||
fun open()
|
||||
fun handleClick(player: Player, item: ItemStack, slot: Int)
|
||||
}
|
||||
|
||||
class MainMenu(private val plugin: CctvPlugin, private val player: Player) : CctvMenu {
|
||||
private val inventory: Inventory = Bukkit.createInventory(this, 27, "CCTV System")
|
||||
|
||||
override fun getInventory(): Inventory = inventory
|
||||
|
||||
override fun open() {
|
||||
// Create menu items
|
||||
val placeCameraItem = createItem(Material.ENDER_EYE, "§aPlace New Camera", listOf("§7Click to place a camera", "§7at your current location"))
|
||||
val viewCamerasItem = createItem(Material.SPYGLASS, "§bView Cameras", listOf("§7Click to view available cameras"))
|
||||
val manageCamerasItem = createItem(Material.REDSTONE_BLOCK, "§cManage Your Cameras", listOf("§7Click to manage your cameras"))
|
||||
|
||||
// Place items in inventory
|
||||
inventory.setItem(11, placeCameraItem)
|
||||
inventory.setItem(13, viewCamerasItem)
|
||||
inventory.setItem(15, manageCamerasItem)
|
||||
|
||||
// Open inventory for player
|
||||
player.openInventory(inventory)
|
||||
}
|
||||
|
||||
override fun handleClick(player: Player, item: ItemStack, slot: Int) {
|
||||
when (slot) {
|
||||
11 -> {
|
||||
if (player.hasPermission("cctv.place")) {
|
||||
player.closeInventory()
|
||||
PlaceCameraMenu(plugin, player).open()
|
||||
} else {
|
||||
player.sendMessage("§cYou don't have permission to place cameras.")
|
||||
}
|
||||
}
|
||||
13 -> {
|
||||
player.closeInventory()
|
||||
ViewCamerasMenu(plugin, player).open()
|
||||
}
|
||||
15 -> {
|
||||
player.closeInventory()
|
||||
ManageCamerasMenu(plugin, player).open()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun createItem(material: Material, name: String, lore: List<String>): ItemStack {
|
||||
val item = ItemStack(material)
|
||||
val meta = item.itemMeta
|
||||
|
||||
meta?.setDisplayName(name)
|
||||
meta?.lore = lore
|
||||
|
||||
item.itemMeta = meta
|
||||
return item
|
||||
}
|
||||
}
|
||||
|
||||
class PlaceCameraMenu(private val plugin: CctvPlugin, private val player: Player) : CctvMenu {
|
||||
private val inventory: Inventory = Bukkit.createInventory(this, 36, "Place Camera")
|
||||
|
||||
override fun getInventory(): Inventory = inventory
|
||||
|
||||
override fun open() {
|
||||
// Create menu items
|
||||
for (i in 0..7) {
|
||||
val cameraItem = createItem(Material.ENDER_EYE, "§aCamera ${i + 1}", listOf("§7Click to place", "§7Camera ${i + 1} at your location"))
|
||||
inventory.setItem(10 + i, cameraItem)
|
||||
}
|
||||
|
||||
val closeItem = createItem(Material.BARRIER, "§cCancel", listOf("§7Click to return to main menu"))
|
||||
inventory.setItem(31, closeItem)
|
||||
|
||||
// Open inventory for player
|
||||
player.openInventory(inventory)
|
||||
}
|
||||
|
||||
override fun handleClick(player: Player, item: ItemStack, slot: Int) {
|
||||
if (slot in 10..17) {
|
||||
val name = item.itemMeta?.displayName?.replace("§a", "") ?: "Camera"
|
||||
if (plugin.createCamera(player, name)) {
|
||||
player.sendMessage("§aCamera '$name' placed successfully at your location!")
|
||||
} else {
|
||||
player.sendMessage("§cFailed to place camera.")
|
||||
}
|
||||
player.closeInventory()
|
||||
MainMenu(plugin, player).open()
|
||||
} else if (slot == 31) {
|
||||
player.closeInventory()
|
||||
MainMenu(plugin, player).open()
|
||||
}
|
||||
}
|
||||
|
||||
private fun createItem(material: Material, name: String, lore: List<String>): ItemStack {
|
||||
val item = ItemStack(material)
|
||||
val meta = item.itemMeta
|
||||
|
||||
meta?.setDisplayName(name)
|
||||
meta?.lore = lore
|
||||
|
||||
item.itemMeta = meta
|
||||
return item
|
||||
}
|
||||
}
|
||||
|
||||
class ViewCamerasMenu(private val plugin: CctvPlugin, private val player: Player) : CctvMenu {
|
||||
private val inventory: Inventory = Bukkit.createInventory(this, 54, "View Cameras")
|
||||
|
||||
override fun getInventory(): Inventory = inventory
|
||||
|
||||
override fun open() {
|
||||
val cameras = plugin.getCameras()
|
||||
|
||||
if (cameras.isEmpty()) {
|
||||
val noItem = createItem(Material.BARRIER, "§cNo Cameras Available", listOf("§7No cameras have been placed yet"))
|
||||
inventory.setItem(22, noItem)
|
||||
} else {
|
||||
var slot = 0
|
||||
cameras.forEach { (id, camera) ->
|
||||
if (slot < 45) {
|
||||
val cameraItem = createItem(
|
||||
Material.ENDER_EYE,
|
||||
"§a${camera.name}",
|
||||
listOf(
|
||||
"§7ID: §f$id",
|
||||
"§7Location: §f${camera.location.world?.name} (${camera.location.blockX}, ${camera.location.blockY}, ${camera.location.blockZ})",
|
||||
"§7Owner: §f${Bukkit.getOfflinePlayer(camera.owner).name ?: "Unknown"}",
|
||||
"",
|
||||
"§eClick to view this camera"
|
||||
)
|
||||
)
|
||||
|
||||
// Store camera ID in item NBT
|
||||
val meta = cameraItem.itemMeta
|
||||
meta?.persistentDataContainer?.set(
|
||||
org.bukkit.NamespacedKey(plugin, "camera-id"),
|
||||
org.bukkit.persistence.PersistentDataType.STRING,
|
||||
id
|
||||
)
|
||||
cameraItem.itemMeta = meta
|
||||
|
||||
inventory.setItem(slot, cameraItem)
|
||||
slot++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val backItem = createItem(Material.ARROW, "§cBack", listOf("§7Return to main menu"))
|
||||
inventory.setItem(49, backItem)
|
||||
|
||||
// Open inventory for player
|
||||
player.openInventory(inventory)
|
||||
}
|
||||
|
||||
override fun handleClick(player: Player, item: ItemStack, slot: Int) {
|
||||
if (slot == 49) {
|
||||
player.closeInventory()
|
||||
MainMenu(plugin, player).open()
|
||||
return
|
||||
}
|
||||
|
||||
if (item.type == Material.ENDER_EYE) {
|
||||
val meta = item.itemMeta ?: return
|
||||
val cameraId = meta.persistentDataContainer.get(
|
||||
org.bukkit.NamespacedKey(plugin, "camera-id"),
|
||||
org.bukkit.persistence.PersistentDataType.STRING
|
||||
) ?: return
|
||||
|
||||
player.closeInventory()
|
||||
if (plugin.startViewing(player, cameraId)) {
|
||||
player.sendMessage("§aViewing camera feed. Type /cctv to return.")
|
||||
} else {
|
||||
player.sendMessage("§cFailed to view camera.")
|
||||
MainMenu(plugin, player).open()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun createItem(material: Material, name: String, lore: List<String>): ItemStack {
|
||||
val item = ItemStack(material)
|
||||
val meta = item.itemMeta
|
||||
|
||||
meta?.setDisplayName(name)
|
||||
meta?.lore = lore
|
||||
|
||||
item.itemMeta = meta
|
||||
return item
|
||||
}
|
||||
}
|
||||
|
||||
class ManageCamerasMenu(private val plugin: CctvPlugin, private val player: Player) : CctvMenu {
|
||||
private val inventory: Inventory = Bukkit.createInventory(this, 54, "Manage Cameras")
|
||||
|
||||
override fun getInventory(): Inventory = inventory
|
||||
|
||||
override fun open() {
|
||||
val cameras = plugin.getCameras().filter {
|
||||
it.value.owner == player.uniqueId || player.hasPermission("cctv.admin")
|
||||
}
|
||||
|
||||
if (cameras.isEmpty()) {
|
||||
val noItem = createItem(Material.BARRIER, "§cNo Cameras Available", listOf("§7You don't have any cameras to manage"))
|
||||
inventory.setItem(22, noItem)
|
||||
} else {
|
||||
var slot = 0
|
||||
cameras.forEach { (id, camera) ->
|
||||
if (slot < 45) {
|
||||
val cameraItem = createItem(
|
||||
Material.ENDER_EYE,
|
||||
"§a${camera.name}",
|
||||
listOf(
|
||||
"§7ID: §f$id",
|
||||
"§7Location: §f${camera.location.world?.name} (${camera.location.blockX}, ${camera.location.blockY}, ${camera.location.blockZ})",
|
||||
"",
|
||||
"§eClick to remove this camera"
|
||||
)
|
||||
)
|
||||
|
||||
// Store camera ID in item NBT
|
||||
val meta = cameraItem.itemMeta
|
||||
meta?.persistentDataContainer?.set(
|
||||
org.bukkit.NamespacedKey(plugin, "camera-id"),
|
||||
org.bukkit.persistence.PersistentDataType.STRING,
|
||||
id
|
||||
)
|
||||
cameraItem.itemMeta = meta
|
||||
|
||||
inventory.setItem(slot, cameraItem)
|
||||
slot++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val backItem = createItem(Material.ARROW, "§cBack", listOf("§7Return to main menu"))
|
||||
inventory.setItem(49, backItem)
|
||||
|
||||
// Open inventory for player
|
||||
player.openInventory(inventory)
|
||||
}
|
||||
|
||||
override fun handleClick(player: Player, item: ItemStack, slot: Int) {
|
||||
if (slot == 49) {
|
||||
player.closeInventory()
|
||||
MainMenu(plugin, player).open()
|
||||
return
|
||||
}
|
||||
|
||||
if (item.type == Material.ENDER_EYE) {
|
||||
val meta = item.itemMeta ?: return
|
||||
val cameraId = meta.persistentDataContainer.get(
|
||||
org.bukkit.NamespacedKey(plugin, "camera-id"),
|
||||
org.bukkit.persistence.PersistentDataType.STRING
|
||||
) ?: return
|
||||
|
||||
player.closeInventory()
|
||||
if (plugin.removeCamera(cameraId, player)) {
|
||||
player.sendMessage("§aCamera removed successfully!")
|
||||
} else {
|
||||
player.sendMessage("§cFailed to remove camera. Make sure you own it or have admin permissions.")
|
||||
}
|
||||
ManageCamerasMenu(plugin, player).open()
|
||||
}
|
||||
}
|
||||
|
||||
private fun createItem(material: Material, name: String, lore: List<String>): ItemStack {
|
||||
val item = ItemStack(material)
|
||||
val meta = item.itemMeta
|
||||
|
||||
meta?.setDisplayName(name)
|
||||
meta?.lore = lore
|
||||
|
||||
item.itemMeta = meta
|
||||
return item
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
cameras:
|
||||
## data is here automatically
|
||||
@@ -0,0 +1,4 @@
|
||||
settings:
|
||||
max-cameras-per-player: 10
|
||||
camera-range: 50 # Maximum viewing distance
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
name: CctvPlugin
|
||||
version: 1.0.0
|
||||
main: com.example.cctv.CctvPlugin
|
||||
api-version: '1.21'
|
||||
description: CCTV camera system for Minecraft
|
||||
author: YourName
|
||||
commands:
|
||||
cctv:
|
||||
description: Manage CCTV cameras
|
||||
usage: /cctv [place|list|remove|help]
|
||||
permission: cctv.use
|
||||
permissions:
|
||||
cctv.use:
|
||||
description: Allows using the CCTV system
|
||||
default: true
|
||||
cctv.place:
|
||||
description: Allows placing cameras
|
||||
default: op
|
||||
cctv.admin:
|
||||
description: Allows managing all cameras
|
||||
default: op
|
||||
children:
|
||||
cctv.use: true
|
||||
cctv.place: true
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,2 @@
|
||||
cameras:
|
||||
## data is here automatically
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,4 @@
|
||||
settings:
|
||||
max-cameras-per-player: 10
|
||||
camera-range: 50 # Maximum viewing distance
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
name: CctvPlugin
|
||||
version: 1.0.0
|
||||
main: com.example.cctv.CctvPlugin
|
||||
api-version: '1.21'
|
||||
description: CCTV camera system for Minecraft
|
||||
author: YourName
|
||||
commands:
|
||||
cctv:
|
||||
description: Manage CCTV cameras
|
||||
usage: /cctv [place|list|remove|help]
|
||||
permission: cctv.use
|
||||
permissions:
|
||||
cctv.use:
|
||||
description: Allows using the CCTV system
|
||||
default: true
|
||||
cctv.place:
|
||||
description: Allows placing cameras
|
||||
default: op
|
||||
cctv.admin:
|
||||
description: Allows managing all cameras
|
||||
default: op
|
||||
children:
|
||||
cctv.use: true
|
||||
cctv.place: true
|
||||
@@ -0,0 +1,3 @@
|
||||
artifactId=CctvPlugin
|
||||
groupId=org.CctvPlugin
|
||||
version=1.0-SNAPSHOT
|
||||
Binary file not shown.
Reference in New Issue
Block a user