initial
This commit is contained in:
5
.idea/.gitignore
generated
vendored
Normal file
5
.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Environment-dependent path to Maven home directory
|
||||
/mavenHomeManager.xml
|
||||
13
.idea/compiler.xml
generated
Normal file
13
.idea/compiler.xml
generated
Normal file
@@ -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>
|
||||
7
.idea/encodings.xml
generated
Normal file
7
.idea/encodings.xml
generated
Normal file
@@ -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>
|
||||
30
.idea/jarRepositories.xml
generated
Normal file
30
.idea/jarRepositories.xml
generated
Normal file
@@ -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>
|
||||
6
.idea/kotlinc.xml
generated
Normal file
6
.idea/kotlinc.xml
generated
Normal file
@@ -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>
|
||||
12
.idea/material_theme_project_new.xml
generated
Normal file
12
.idea/material_theme_project_new.xml
generated
Normal file
@@ -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>
|
||||
14
.idea/misc.xml
generated
Normal file
14
.idea/misc.xml
generated
Normal file
@@ -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>
|
||||
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
@@ -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>
|
||||
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
@@ -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>
|
||||
13
CctvPlugin.iml
Normal file
13
CctvPlugin.iml
Normal file
@@ -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>
|
||||
72
dependency-reduced-pom.xml
Normal file
72
dependency-reduced-pom.xml
Normal file
@@ -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>
|
||||
87
pom.xml
Normal file
87
pom.xml
Normal file
@@ -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>
|
||||
115
src/main/kotlin/org/CctvPlugin/cctvPlugin/CctvCommand.kt
Normal file
115
src/main/kotlin/org/CctvPlugin/cctvPlugin/CctvCommand.kt
Normal file
@@ -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()
|
||||
}
|
||||
}
|
||||
235
src/main/kotlin/org/CctvPlugin/cctvPlugin/CctvPlugin.kt
Normal file
235
src/main/kotlin/org/CctvPlugin/cctvPlugin/CctvPlugin.kt
Normal file
@@ -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()
|
||||
)
|
||||
}
|
||||
288
src/main/kotlin/org/CctvPlugin/cctvPlugin/Menus.kt
Normal file
288
src/main/kotlin/org/CctvPlugin/cctvPlugin/Menus.kt
Normal file
@@ -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
|
||||
}
|
||||
}
|
||||
2
src/main/resources/cameras.yml
Normal file
2
src/main/resources/cameras.yml
Normal file
@@ -0,0 +1,2 @@
|
||||
cameras:
|
||||
## data is here automatically
|
||||
4
src/main/resources/config.yml
Normal file
4
src/main/resources/config.yml
Normal file
@@ -0,0 +1,4 @@
|
||||
settings:
|
||||
max-cameras-per-player: 10
|
||||
camera-range: 50 # Maximum viewing distance
|
||||
|
||||
24
src/main/resources/plugin.yml
Normal file
24
src/main/resources/plugin.yml
Normal file
@@ -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
|
||||
BIN
target/CctvPlugin-1.0-SNAPSHOT.jar
Normal file
BIN
target/CctvPlugin-1.0-SNAPSHOT.jar
Normal file
Binary file not shown.
BIN
target/classes/META-INF/CctvPlugin.kotlin_module
Normal file
BIN
target/classes/META-INF/CctvPlugin.kotlin_module
Normal file
Binary file not shown.
2
target/classes/cameras.yml
Normal file
2
target/classes/cameras.yml
Normal file
@@ -0,0 +1,2 @@
|
||||
cameras:
|
||||
## data is here automatically
|
||||
BIN
target/classes/com/example/cctv/CctvCommand.class
Normal file
BIN
target/classes/com/example/cctv/CctvCommand.class
Normal file
Binary file not shown.
BIN
target/classes/com/example/cctv/CctvPlugin$Camera.class
Normal file
BIN
target/classes/com/example/cctv/CctvPlugin$Camera.class
Normal file
Binary file not shown.
BIN
target/classes/com/example/cctv/CctvPlugin.class
Normal file
BIN
target/classes/com/example/cctv/CctvPlugin.class
Normal file
Binary file not shown.
BIN
target/classes/com/example/cctv/menus/CctvMenu.class
Normal file
BIN
target/classes/com/example/cctv/menus/CctvMenu.class
Normal file
Binary file not shown.
BIN
target/classes/com/example/cctv/menus/MainMenu.class
Normal file
BIN
target/classes/com/example/cctv/menus/MainMenu.class
Normal file
Binary file not shown.
BIN
target/classes/com/example/cctv/menus/ManageCamerasMenu.class
Normal file
BIN
target/classes/com/example/cctv/menus/ManageCamerasMenu.class
Normal file
Binary file not shown.
BIN
target/classes/com/example/cctv/menus/PlaceCameraMenu.class
Normal file
BIN
target/classes/com/example/cctv/menus/PlaceCameraMenu.class
Normal file
Binary file not shown.
BIN
target/classes/com/example/cctv/menus/ViewCamerasMenu.class
Normal file
BIN
target/classes/com/example/cctv/menus/ViewCamerasMenu.class
Normal file
Binary file not shown.
4
target/classes/config.yml
Normal file
4
target/classes/config.yml
Normal file
@@ -0,0 +1,4 @@
|
||||
settings:
|
||||
max-cameras-per-player: 10
|
||||
camera-range: 50 # Maximum viewing distance
|
||||
|
||||
24
target/classes/plugin.yml
Normal file
24
target/classes/plugin.yml
Normal file
@@ -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
|
||||
3
target/maven-archiver/pom.properties
Normal file
3
target/maven-archiver/pom.properties
Normal file
@@ -0,0 +1,3 @@
|
||||
artifactId=CctvPlugin
|
||||
groupId=org.CctvPlugin
|
||||
version=1.0-SNAPSHOT
|
||||
BIN
target/original-CctvPlugin-1.0-SNAPSHOT.jar
Normal file
BIN
target/original-CctvPlugin-1.0-SNAPSHOT.jar
Normal file
Binary file not shown.
Reference in New Issue
Block a user