mirror of
https://github.com/Pumpkin-MC/Pumpkin.git
synced 2026-08-30 20:14:23 +00:00
Extractor: Parse and use screens.json
This commit is contained in:
102
assets/screens.json
Normal file
102
assets/screens.json
Normal file
@@ -0,0 +1,102 @@
|
||||
[
|
||||
{
|
||||
"id": 0,
|
||||
"name": "minecraft:generic_9x1"
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"name": "minecraft:generic_9x2"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "minecraft:generic_9x3"
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"name": "minecraft:generic_9x4"
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"name": "minecraft:generic_9x5"
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"name": "minecraft:generic_9x6"
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"name": "minecraft:generic_3x3"
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"name": "minecraft:crafter_3x3"
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"name": "minecraft:anvil"
|
||||
},
|
||||
{
|
||||
"id": 9,
|
||||
"name": "minecraft:beacon"
|
||||
},
|
||||
{
|
||||
"id": 10,
|
||||
"name": "minecraft:blast_furnace"
|
||||
},
|
||||
{
|
||||
"id": 11,
|
||||
"name": "minecraft:brewing_stand"
|
||||
},
|
||||
{
|
||||
"id": 12,
|
||||
"name": "minecraft:crafting"
|
||||
},
|
||||
{
|
||||
"id": 13,
|
||||
"name": "minecraft:enchantment"
|
||||
},
|
||||
{
|
||||
"id": 14,
|
||||
"name": "minecraft:furnace"
|
||||
},
|
||||
{
|
||||
"id": 15,
|
||||
"name": "minecraft:grindstone"
|
||||
},
|
||||
{
|
||||
"id": 16,
|
||||
"name": "minecraft:hopper"
|
||||
},
|
||||
{
|
||||
"id": 17,
|
||||
"name": "minecraft:lectern"
|
||||
},
|
||||
{
|
||||
"id": 18,
|
||||
"name": "minecraft:loom"
|
||||
},
|
||||
{
|
||||
"id": 19,
|
||||
"name": "minecraft:merchant"
|
||||
},
|
||||
{
|
||||
"id": 20,
|
||||
"name": "minecraft:shulker_box"
|
||||
},
|
||||
{
|
||||
"id": 21,
|
||||
"name": "minecraft:smithing"
|
||||
},
|
||||
{
|
||||
"id": 22,
|
||||
"name": "minecraft:smoker"
|
||||
},
|
||||
{
|
||||
"id": 23,
|
||||
"name": "minecraft:cartography_table"
|
||||
},
|
||||
{
|
||||
"id": 24,
|
||||
"name": "minecraft:stonecutter"
|
||||
}
|
||||
]
|
||||
@@ -28,6 +28,7 @@ class Extractor : ModInitializer {
|
||||
Particles(),
|
||||
SyncedRegistries(),
|
||||
Packet(),
|
||||
Screen(),
|
||||
Items(),
|
||||
Blocks(),
|
||||
)
|
||||
|
||||
@@ -4,7 +4,6 @@ import com.google.gson.JsonArray
|
||||
import com.google.gson.JsonElement
|
||||
import com.google.gson.JsonObject
|
||||
import de.snowii.extractor.Extractor
|
||||
import net.minecraft.network.packet.s2c.play.InventoryS2CPacket
|
||||
import net.minecraft.registry.Registries
|
||||
import net.minecraft.server.MinecraftServer
|
||||
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package de.snowii.extractor.extractors
|
||||
|
||||
import com.google.gson.JsonArray
|
||||
import com.google.gson.JsonElement
|
||||
import com.google.gson.JsonObject
|
||||
import de.snowii.extractor.Extractor
|
||||
import net.minecraft.registry.Registries
|
||||
import net.minecraft.server.MinecraftServer
|
||||
|
||||
class Screen : Extractor.Extractor {
|
||||
override fun fileName(): String {
|
||||
return "screens.json"
|
||||
}
|
||||
|
||||
override fun extract(server: MinecraftServer): JsonElement {
|
||||
val screensJson = JsonArray()
|
||||
for (screen in Registries.SCREEN_HANDLER) {
|
||||
val screenJson = JsonObject()
|
||||
screenJson.addProperty("id", Registries.SCREEN_HANDLER.getRawId(screen))
|
||||
screenJson.addProperty("name", Registries.SCREEN_HANDLER.getId(screen)!!.toString())
|
||||
screensJson.add(screenJson)
|
||||
}
|
||||
|
||||
return screensJson
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ edition.workspace = true
|
||||
[dependencies]
|
||||
# For items
|
||||
pumpkin-world = { path = "../pumpkin-world" }
|
||||
pumpkin-macros = { path = "../pumpkin-macros" }
|
||||
|
||||
log.workspace = true
|
||||
itertools.workspace = true
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use crate::container_click::MouseClick;
|
||||
use crate::player::PlayerInventory;
|
||||
use num_derive::{FromPrimitive, ToPrimitive};
|
||||
use pumpkin_macros::screen;
|
||||
use pumpkin_world::item::ItemStack;
|
||||
|
||||
pub mod container_click;
|
||||
@@ -15,42 +16,43 @@ pub use open_container::OpenContainer;
|
||||
|
||||
/// https://wiki.vg/Inventory
|
||||
#[derive(Debug, ToPrimitive, FromPrimitive, Clone, Copy, Eq, PartialEq)]
|
||||
#[repr(u16)]
|
||||
pub enum WindowType {
|
||||
// not used
|
||||
Generic9x1,
|
||||
Generic9x1 = screen!("minecraft:generic_9x1"),
|
||||
// not used
|
||||
Generic9x2,
|
||||
Generic9x2 = screen!("minecraft:generic_9x2"),
|
||||
// General-purpose 3-row inventory. Used by Chest, minecart with chest, ender chest, and barrel
|
||||
Generic9x3,
|
||||
Generic9x3 = screen!("minecraft:generic_9x3"),
|
||||
// not used
|
||||
Generic9x4,
|
||||
Generic9x4 = screen!("minecraft:generic_9x4"),
|
||||
// not used
|
||||
Generic9x5,
|
||||
Generic9x5 = screen!("minecraft:generic_9x5"),
|
||||
// Used by large chests
|
||||
Generic9x6,
|
||||
Generic9x6 = screen!("minecraft:generic_9x6"),
|
||||
// General-purpose 3-by-3 square inventory, used by Dispenser and Dropper
|
||||
Generic3x3,
|
||||
Generic3x3 = screen!("minecraft:generic_3x3"),
|
||||
// General-purpose 3-by-3 square inventory, used by the Crafter
|
||||
Craft3x3,
|
||||
Anvil,
|
||||
Beacon,
|
||||
BlastFurnace,
|
||||
BrewingStand,
|
||||
CraftingTable,
|
||||
EnchantmentTable,
|
||||
Furnace,
|
||||
Grindstone,
|
||||
Craft3x3 = screen!("minecraft:crafter_3x3"),
|
||||
Anvil = screen!("minecraft:anvil"),
|
||||
Beacon = screen!("minecraft:beacon"),
|
||||
BlastFurnace = screen!("minecraft:blast_furnace"),
|
||||
BrewingStand = screen!("minecraft:brewing_stand"),
|
||||
CraftingTable = screen!("minecraft:crafting"),
|
||||
EnchantmentTable = screen!("minecraft:enchantment"),
|
||||
Furnace = screen!("minecraft:furnace"),
|
||||
Grindstone = screen!("minecraft:grindstone"),
|
||||
// Hopper or minecart with hopper
|
||||
Hopper,
|
||||
Lectern,
|
||||
Loom,
|
||||
Hopper = screen!("minecraft:hopper"),
|
||||
Lectern = screen!("minecraft:lectern"),
|
||||
Loom = screen!("minecraft:loom"),
|
||||
// Villager, Wandering Trader
|
||||
Merchant,
|
||||
ShulkerBox,
|
||||
SmithingTable,
|
||||
Smoker,
|
||||
CartographyTable,
|
||||
Stonecutter,
|
||||
Merchant = screen!("minecraft:merchant"),
|
||||
ShulkerBox = screen!("minecraft:shulker_box"),
|
||||
SmithingTable = screen!("minecraft:smithing"),
|
||||
Smoker = screen!("minecraft:smoker"),
|
||||
CartographyTable = screen!("minecraft:cartography_table"),
|
||||
Stonecutter = screen!("minecraft:stonecutter"),
|
||||
}
|
||||
pub struct ContainerStruct<const SLOTS: usize>([Option<ItemStack>; SLOTS]);
|
||||
|
||||
|
||||
@@ -25,6 +25,11 @@ pub fn client_packet(input: TokenStream, item: TokenStream) -> TokenStream {
|
||||
gen.into()
|
||||
}
|
||||
|
||||
mod screen;
|
||||
#[proc_macro]
|
||||
pub fn screen(item: TokenStream) -> TokenStream {
|
||||
screen::screen_impl(item)
|
||||
}
|
||||
mod sound;
|
||||
#[proc_macro]
|
||||
pub fn sound(item: TokenStream) -> TokenStream {
|
||||
|
||||
27
pumpkin-macros/src/screen.rs
Normal file
27
pumpkin-macros/src/screen.rs
Normal file
@@ -0,0 +1,27 @@
|
||||
use std::{collections::HashMap, sync::LazyLock};
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct Screen {
|
||||
name: String,
|
||||
id: u16,
|
||||
}
|
||||
|
||||
static SCREENS: LazyLock<HashMap<String, u16>> = LazyLock::new(|| {
|
||||
serde_json::from_str::<Vec<Screen>>(include_str!("../../assets/screens.json"))
|
||||
.expect("Could not parse screens.json registry.")
|
||||
.into_iter()
|
||||
.map(|val| (val.name, val.id))
|
||||
.collect()
|
||||
});
|
||||
|
||||
pub(crate) fn screen_impl(item: TokenStream) -> TokenStream {
|
||||
let input_string = item.to_string();
|
||||
let screen_name = input_string.trim_matches('"');
|
||||
|
||||
let id = SCREENS.get(screen_name).expect("Invalid screen");
|
||||
quote! { #id }.into()
|
||||
}
|
||||
@@ -60,7 +60,7 @@ impl AttackType {
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) async fn handle_knockback(
|
||||
pub async fn handle_knockback(
|
||||
attacker_entity: &Entity,
|
||||
victim: &Player,
|
||||
victim_entity: &Entity,
|
||||
@@ -93,11 +93,7 @@ pub(super) async fn handle_knockback(
|
||||
victim.client.send_packet(packet).await;
|
||||
}
|
||||
|
||||
pub(super) async fn spawn_sweep_particle(
|
||||
attacker_entity: &Entity,
|
||||
world: &World,
|
||||
pos: &Vector3<f64>,
|
||||
) {
|
||||
pub async fn spawn_sweep_particle(attacker_entity: &Entity, world: &World, pos: &Vector3<f64>) {
|
||||
let yaw = attacker_entity.yaw.load();
|
||||
let d = -f64::from((yaw * (PI / 180.0)).sin());
|
||||
let e = f64::from((yaw * (PI / 180.0)).cos());
|
||||
@@ -123,11 +119,7 @@ pub(super) async fn spawn_sweep_particle(
|
||||
.await;
|
||||
}
|
||||
|
||||
pub(super) async fn player_attack_sound(
|
||||
pos: &Vector3<f64>,
|
||||
world: &World,
|
||||
attack_type: AttackType,
|
||||
) {
|
||||
pub async fn player_attack_sound(pos: &Vector3<f64>, world: &World, attack_type: AttackType) {
|
||||
match attack_type {
|
||||
AttackType::Knockback => {
|
||||
world
|
||||
|
||||
@@ -8,8 +8,8 @@ use pumpkin_inventory::container_click::{
|
||||
};
|
||||
use pumpkin_inventory::drag_handler::DragHandler;
|
||||
use pumpkin_inventory::window_property::{WindowProperty, WindowPropertyTrait};
|
||||
use pumpkin_inventory::Container;
|
||||
use pumpkin_inventory::{container_click, InventoryError, OptionallyCombinedContainer};
|
||||
use pumpkin_inventory::{Container, WindowType};
|
||||
use pumpkin_protocol::client::play::{
|
||||
CCloseContainer, COpenScreen, CSetContainerContent, CSetContainerProperty, CSetContainerSlot,
|
||||
};
|
||||
@@ -22,7 +22,7 @@ use std::sync::Arc;
|
||||
#[expect(unused)]
|
||||
|
||||
impl Player {
|
||||
pub async fn open_container(&self, server: &Server, minecraft_menu_id: &str) {
|
||||
pub async fn open_container(&self, server: &Server, window_type: WindowType) {
|
||||
let inventory = self.inventory.lock().await;
|
||||
inventory
|
||||
.state_id
|
||||
@@ -30,17 +30,7 @@ impl Player {
|
||||
let total_opened_containers = inventory.total_opened_containers;
|
||||
let container = self.get_open_container(server);
|
||||
let container = container.as_ref().map(|container| container.lock());
|
||||
// let menu_protocol_id = (*pumpkin_world::global_registry::REGISTRY
|
||||
// .get("minecraft:menu")
|
||||
// .unwrap()
|
||||
// .entries
|
||||
// .get(minecraft_menu_id)
|
||||
// .expect("Should be a valid menu id")
|
||||
// .get("protocol_id")
|
||||
// .unwrap())
|
||||
// .into();
|
||||
// TODO
|
||||
let menu_protocol_id = VarInt(0);
|
||||
let window_title = match container {
|
||||
Some(container) => container.await.window_name(),
|
||||
None => inventory.window_name(),
|
||||
@@ -50,7 +40,7 @@ impl Player {
|
||||
self.client
|
||||
.send_packet(&COpenScreen::new(
|
||||
total_opened_containers.into(),
|
||||
menu_protocol_id,
|
||||
VarInt(window_type as i32),
|
||||
title,
|
||||
))
|
||||
.await;
|
||||
|
||||
@@ -43,7 +43,7 @@ use thiserror::Error;
|
||||
|
||||
pub mod authentication;
|
||||
mod client_packet;
|
||||
mod combat;
|
||||
pub mod combat;
|
||||
mod container;
|
||||
pub mod player_packet;
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ use crate::{
|
||||
world::player_chunker,
|
||||
};
|
||||
use num_traits::FromPrimitive;
|
||||
use pumpkin_config::{PVPConfig, ADVANCED_CONFIG};
|
||||
use pumpkin_config::ADVANCED_CONFIG;
|
||||
use pumpkin_core::math::position::WorldPosition;
|
||||
use pumpkin_core::{
|
||||
math::{vector3::Vector3, wrap_degrees},
|
||||
@@ -15,12 +15,11 @@ use pumpkin_core::{
|
||||
GameMode,
|
||||
};
|
||||
use pumpkin_inventory::{InventoryError, WindowType};
|
||||
use pumpkin_macros::sound;
|
||||
use pumpkin_protocol::server::play::{SCloseContainer, SKeepAlive, SSetPlayerGround, SUseItem};
|
||||
use pumpkin_protocol::{
|
||||
client::play::{
|
||||
Animation, CAcknowledgeBlockChange, CEntityAnimation, CHeadRot, CHurtAnimation,
|
||||
CPingResponse, CPlayerChatMessage, CUpdateEntityPos, CUpdateEntityPosRot, CUpdateEntityRot,
|
||||
FilterType,
|
||||
Animation, CAcknowledgeBlockChange, CEntityAnimation, CHeadRot, CPingResponse,
|
||||
CPlayerChatMessage, CUpdateEntityPos, CUpdateEntityPosRot, CUpdateEntityRot, FilterType,
|
||||
},
|
||||
server::play::{
|
||||
Action, ActionType, SChatCommand, SChatMessage, SClientInformationPlay, SConfirmTeleport,
|
||||
@@ -28,18 +27,10 @@ use pumpkin_protocol::{
|
||||
SPlayerPosition, SPlayerPositionRotation, SPlayerRotation, SSetCreativeSlot, SSetHeldItem,
|
||||
SSwingArm, SUseItemOn, Status,
|
||||
},
|
||||
SoundCategory,
|
||||
};
|
||||
use pumpkin_protocol::{
|
||||
server::play::{SCloseContainer, SKeepAlive, SSetPlayerGround, SUseItem},
|
||||
VarInt,
|
||||
};
|
||||
use pumpkin_world::block::{block_registry::get_block_by_item, BlockFace};
|
||||
|
||||
use super::{
|
||||
combat::{self, player_attack_sound, AttackType},
|
||||
PlayerConfig,
|
||||
};
|
||||
use super::PlayerConfig;
|
||||
|
||||
fn modulus(a: f32, b: f32) -> f32 {
|
||||
((a % b) + b) % b
|
||||
@@ -435,7 +426,7 @@ impl Player {
|
||||
return;
|
||||
};
|
||||
|
||||
self.attack(&victim, config).await;
|
||||
self.attack(&victim).await;
|
||||
}
|
||||
ActionType::Interact | ActionType::InteractAt => {
|
||||
log::debug!("todo");
|
||||
@@ -443,72 +434,6 @@ impl Player {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn attack(&self, victim: &Arc<Self>, config: &PVPConfig) {
|
||||
let world = &self.living_entity.entity.world;
|
||||
let victim_entity = &victim.living_entity.entity;
|
||||
let attacker_entity = &self.living_entity.entity;
|
||||
|
||||
let pos = victim_entity.pos.load();
|
||||
|
||||
let attack_cooldown_progress = self.get_attack_cooldown_progress(0.5);
|
||||
self.last_attacked_ticks
|
||||
.store(0, std::sync::atomic::Ordering::Relaxed);
|
||||
|
||||
// TODO: attack damage attribute and deal damage
|
||||
let damage = 2.0;
|
||||
if !victim.living_entity.damage(damage)
|
||||
|| (config.protect_creative && victim.gamemode.load() == GameMode::Creative)
|
||||
{
|
||||
world
|
||||
.play_sound(
|
||||
sound!("minecraft:entity.player.attack.nodamage"),
|
||||
SoundCategory::Players,
|
||||
&pos,
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
|
||||
world
|
||||
.play_sound(
|
||||
sound!("minecraft:entity.player.hurt"),
|
||||
SoundCategory::Players,
|
||||
&pos,
|
||||
)
|
||||
.await;
|
||||
|
||||
let attack_type = AttackType::new(self, attack_cooldown_progress).await;
|
||||
|
||||
player_attack_sound(&pos, world, attack_type).await;
|
||||
|
||||
// if is_crit {
|
||||
// damage *= 1.5;
|
||||
// }
|
||||
|
||||
let mut knockback_strength = 1.0;
|
||||
match attack_type {
|
||||
AttackType::Knockback => knockback_strength += 1.0,
|
||||
AttackType::Sweeping => {
|
||||
combat::spawn_sweep_particle(attacker_entity, world, &pos).await;
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
|
||||
if config.knockback {
|
||||
combat::handle_knockback(attacker_entity, victim, victim_entity, knockback_strength)
|
||||
.await;
|
||||
}
|
||||
|
||||
if config.hurt_animation {
|
||||
let entity_id = VarInt(victim_entity.entity_id);
|
||||
world
|
||||
.broadcast_packet_all(&CHurtAnimation::new(&entity_id, attacker_entity.yaw.load()))
|
||||
.await;
|
||||
}
|
||||
|
||||
if config.swing {}
|
||||
}
|
||||
|
||||
pub async fn handle_player_action(&self, player_action: SPlayerAction) {
|
||||
match Status::from_i32(player_action.status.0) {
|
||||
Some(status) => match status {
|
||||
@@ -674,6 +599,10 @@ impl Player {
|
||||
// This function will in the future be used to keep track of if the client is in a valid state.
|
||||
// But this is not possible yet
|
||||
pub async fn handle_close_container(&self, server: &Server, packet: SCloseContainer) {
|
||||
let Some(_window_type) = WindowType::from_i32(packet.window_id.0) else {
|
||||
self.kick(TextComponent::text("Invalid window ID")).await;
|
||||
return;
|
||||
};
|
||||
// window_id 0 represents both 9x1 Generic AND inventory here
|
||||
self.inventory
|
||||
.lock()
|
||||
@@ -688,9 +617,5 @@ impl Player {
|
||||
}
|
||||
self.open_container.store(None);
|
||||
}
|
||||
let Some(_window_type) = WindowType::from_i32(packet.window_id.0) else {
|
||||
self.kick(TextComponent::text("Invalid window ID")).await;
|
||||
return;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,9 @@ impl CommandExecutor for EchestExecutor {
|
||||
open_containers.insert(0, open_container);
|
||||
}
|
||||
}
|
||||
player.open_container(server, "minecraft:generic_9x3").await;
|
||||
player
|
||||
.open_container(server, pumpkin_inventory::WindowType::Generic9x3)
|
||||
.await;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -11,6 +11,7 @@ use crossbeam::atomic::AtomicCell;
|
||||
use itertools::Itertools;
|
||||
use num_derive::FromPrimitive;
|
||||
use num_traits::{FromPrimitive, ToPrimitive};
|
||||
use pumpkin_config::ADVANCED_CONFIG;
|
||||
use pumpkin_core::{
|
||||
math::{boundingbox::BoundingBox, position::WorldPosition, vector2::Vector2, vector3::Vector3},
|
||||
text::TextComponent,
|
||||
@@ -18,11 +19,13 @@ use pumpkin_core::{
|
||||
};
|
||||
use pumpkin_entity::{entity_type::EntityType, EntityId};
|
||||
use pumpkin_inventory::player::PlayerInventory;
|
||||
use pumpkin_macros::sound;
|
||||
use pumpkin_protocol::{
|
||||
bytebuf::DeserializerError,
|
||||
client::play::{
|
||||
CGameEvent, CKeepAlive, CPlayDisconnect, CPlayerAbilities, CPlayerInfoUpdate, CSetHealth,
|
||||
CSyncPlayerPosition, CSystemChatMessage, GameEvent, PlayerAction,
|
||||
CGameEvent, CHurtAnimation, CKeepAlive, CPlayDisconnect, CPlayerAbilities,
|
||||
CPlayerInfoUpdate, CSetHealth, CSyncPlayerPosition, CSystemChatMessage, GameEvent,
|
||||
PlayerAction,
|
||||
},
|
||||
server::play::{
|
||||
SChatCommand, SChatMessage, SClientInformationPlay, SConfirmTeleport, SInteract,
|
||||
@@ -30,7 +33,7 @@ use pumpkin_protocol::{
|
||||
SPlayerRotation, SSetCreativeSlot, SSetHeldItem, SSetPlayerGround, SSwingArm, SUseItem,
|
||||
SUseItemOn, ServerboundPlayPackets,
|
||||
},
|
||||
RawPacket, ServerPacket, VarInt,
|
||||
RawPacket, ServerPacket, SoundCategory, VarInt,
|
||||
};
|
||||
use tokio::sync::{Mutex, Notify};
|
||||
use tokio::task::JoinHandle;
|
||||
@@ -40,7 +43,11 @@ use pumpkin_world::{cylindrical_chunk_iterator::Cylindrical, item::ItemStack};
|
||||
|
||||
use super::Entity;
|
||||
use crate::{
|
||||
client::{authentication::GameProfile, Client, PlayerConfig},
|
||||
client::{
|
||||
authentication::GameProfile,
|
||||
combat::{self, player_attack_sound, AttackType},
|
||||
Client, PlayerConfig,
|
||||
},
|
||||
server::Server,
|
||||
world::World,
|
||||
};
|
||||
@@ -331,6 +338,73 @@ impl Player {
|
||||
//self.living_entity.entity.world.level.list_cached();
|
||||
}
|
||||
|
||||
pub async fn attack(&self, victim: &Arc<Self>) {
|
||||
let world = &self.living_entity.entity.world;
|
||||
let victim_entity = &victim.living_entity.entity;
|
||||
let attacker_entity = &self.living_entity.entity;
|
||||
let config = &ADVANCED_CONFIG.pvp;
|
||||
|
||||
let pos = victim_entity.pos.load();
|
||||
|
||||
let attack_cooldown_progress = self.get_attack_cooldown_progress(0.5);
|
||||
self.last_attacked_ticks
|
||||
.store(0, std::sync::atomic::Ordering::Relaxed);
|
||||
|
||||
// TODO: attack damage attribute and deal damage
|
||||
let damage = 2.0;
|
||||
if !victim.living_entity.damage(damage)
|
||||
|| (config.protect_creative && victim.gamemode.load() == GameMode::Creative)
|
||||
{
|
||||
world
|
||||
.play_sound(
|
||||
sound!("minecraft:entity.player.attack.nodamage"),
|
||||
SoundCategory::Players,
|
||||
&pos,
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
|
||||
world
|
||||
.play_sound(
|
||||
sound!("minecraft:entity.player.hurt"),
|
||||
SoundCategory::Players,
|
||||
&pos,
|
||||
)
|
||||
.await;
|
||||
|
||||
let attack_type = AttackType::new(self, attack_cooldown_progress).await;
|
||||
|
||||
player_attack_sound(&pos, world, attack_type).await;
|
||||
|
||||
// if is_crit {
|
||||
// damage *= 1.5;
|
||||
// }
|
||||
|
||||
let mut knockback_strength = 1.0;
|
||||
match attack_type {
|
||||
AttackType::Knockback => knockback_strength += 1.0,
|
||||
AttackType::Sweeping => {
|
||||
combat::spawn_sweep_particle(attacker_entity, world, &pos).await;
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
|
||||
if config.knockback {
|
||||
combat::handle_knockback(attacker_entity, victim, victim_entity, knockback_strength)
|
||||
.await;
|
||||
}
|
||||
|
||||
if config.hurt_animation {
|
||||
let entity_id = VarInt(victim_entity.entity_id);
|
||||
world
|
||||
.broadcast_packet_all(&CHurtAnimation::new(&entity_id, attacker_entity.yaw.load()))
|
||||
.await;
|
||||
}
|
||||
|
||||
if config.swing {}
|
||||
}
|
||||
|
||||
pub async fn await_cancel(&self) {
|
||||
self.cancel_tasks.notified().await;
|
||||
}
|
||||
|
||||
@@ -3,23 +3,12 @@ use std::net::SocketAddr;
|
||||
use packet::{ClientboundPacket, Packet, PacketError, ServerboundPacket};
|
||||
use pumpkin_config::{RCONConfig, ADVANCED_CONFIG};
|
||||
use std::sync::Arc;
|
||||
use thiserror::Error;
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
|
||||
use crate::server::Server;
|
||||
|
||||
mod packet;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum RCONError {
|
||||
#[error("authentication failed")]
|
||||
Auth,
|
||||
#[error("command exceeds the maximum length")]
|
||||
CommandTooLong,
|
||||
#[error("{}", _0)]
|
||||
Io(std::io::Error),
|
||||
}
|
||||
|
||||
pub struct RCONServer;
|
||||
|
||||
impl RCONServer {
|
||||
|
||||
@@ -147,7 +147,7 @@ impl Server {
|
||||
}
|
||||
|
||||
pub async fn remove_player(&self) {
|
||||
// TODO: Config if we want increase online
|
||||
// TODO: Config if we want decrease online
|
||||
self.server_listing.lock().await.remove_player();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user