mirror of
https://github.com/CitizensDev/Citizens2.git
synced 2024-11-28 13:45:18 +01:00
Implement a lot more methods in custom entity registry to fix tags
This commit is contained in:
parent
b7842183f8
commit
f92fb4373f
@ -578,10 +578,9 @@ public class NMSImpl implements NMSBridge {
|
|||||||
return DEFAULT_SPEED;
|
return DEFAULT_SPEED;
|
||||||
EntityLiving handle = NMSImpl.getHandle((LivingEntity) npc.getEntity());
|
EntityLiving handle = NMSImpl.getHandle((LivingEntity) npc.getEntity());
|
||||||
if (handle == null) {
|
if (handle == null) {
|
||||||
|
return DEFAULT_SPEED;
|
||||||
}
|
}
|
||||||
return DEFAULT_SPEED;
|
return (float) handle.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED).getValue();
|
||||||
// return (float)
|
|
||||||
// handle.getAttributeInstance(GenericAttributes.d).getValue();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -598,10 +598,9 @@ public class NMSImpl implements NMSBridge {
|
|||||||
return DEFAULT_SPEED;
|
return DEFAULT_SPEED;
|
||||||
EntityLiving handle = NMSImpl.getHandle((LivingEntity) npc.getEntity());
|
EntityLiving handle = NMSImpl.getHandle((LivingEntity) npc.getEntity());
|
||||||
if (handle == null) {
|
if (handle == null) {
|
||||||
|
return DEFAULT_SPEED;
|
||||||
}
|
}
|
||||||
return DEFAULT_SPEED;
|
return (float) handle.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED).getValue();
|
||||||
// return (float)
|
|
||||||
// handle.getAttributeInstance(GenericAttributes.d).getValue();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -599,10 +599,9 @@ public class NMSImpl implements NMSBridge {
|
|||||||
return DEFAULT_SPEED;
|
return DEFAULT_SPEED;
|
||||||
EntityLiving handle = NMSImpl.getHandle((LivingEntity) npc.getEntity());
|
EntityLiving handle = NMSImpl.getHandle((LivingEntity) npc.getEntity());
|
||||||
if (handle == null) {
|
if (handle == null) {
|
||||||
|
return DEFAULT_SPEED;
|
||||||
}
|
}
|
||||||
return DEFAULT_SPEED;
|
return (float) handle.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED).getValue();
|
||||||
// return (float)
|
|
||||||
// handle.getAttributeInstance(GenericAttributes.d).getValue();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -628,10 +628,9 @@ public class NMSImpl implements NMSBridge {
|
|||||||
return DEFAULT_SPEED;
|
return DEFAULT_SPEED;
|
||||||
EntityLiving handle = NMSImpl.getHandle((LivingEntity) npc.getEntity());
|
EntityLiving handle = NMSImpl.getHandle((LivingEntity) npc.getEntity());
|
||||||
if (handle == null) {
|
if (handle == null) {
|
||||||
|
return DEFAULT_SPEED;
|
||||||
}
|
}
|
||||||
return DEFAULT_SPEED;
|
return (float) handle.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED).getValue();
|
||||||
// return (float)
|
|
||||||
// handle.getAttributeInstance(GenericAttributes.d).getValue();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -667,10 +667,9 @@ public class NMSImpl implements NMSBridge {
|
|||||||
return DEFAULT_SPEED;
|
return DEFAULT_SPEED;
|
||||||
EntityLiving handle = NMSImpl.getHandle((LivingEntity) npc.getEntity());
|
EntityLiving handle = NMSImpl.getHandle((LivingEntity) npc.getEntity());
|
||||||
if (handle == null) {
|
if (handle == null) {
|
||||||
|
return DEFAULT_SPEED;
|
||||||
}
|
}
|
||||||
return DEFAULT_SPEED;
|
return (float) handle.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED).getValue();
|
||||||
// return (float)
|
|
||||||
// handle.getAttributeInstance(GenericAttributes.d).getValue();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -682,10 +682,9 @@ public class NMSImpl implements NMSBridge {
|
|||||||
return DEFAULT_SPEED;
|
return DEFAULT_SPEED;
|
||||||
EntityLiving handle = NMSImpl.getHandle((LivingEntity) npc.getEntity());
|
EntityLiving handle = NMSImpl.getHandle((LivingEntity) npc.getEntity());
|
||||||
if (handle == null) {
|
if (handle == null) {
|
||||||
|
return DEFAULT_SPEED;
|
||||||
}
|
}
|
||||||
return DEFAULT_SPEED;
|
return (float) handle.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED).getValue();
|
||||||
// return (float)
|
|
||||||
// handle.getAttributeInstance(GenericAttributes.d).getValue();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -703,10 +703,9 @@ public class NMSImpl implements NMSBridge {
|
|||||||
return DEFAULT_SPEED;
|
return DEFAULT_SPEED;
|
||||||
EntityLiving handle = NMSImpl.getHandle((LivingEntity) npc.getEntity());
|
EntityLiving handle = NMSImpl.getHandle((LivingEntity) npc.getEntity());
|
||||||
if (handle == null) {
|
if (handle == null) {
|
||||||
|
return DEFAULT_SPEED;
|
||||||
}
|
}
|
||||||
return DEFAULT_SPEED;
|
return (float) handle.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED).getValue();
|
||||||
// return (float)
|
|
||||||
// handle.getAttributeInstance(GenericAttributes.d).getValue();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -704,11 +704,11 @@ public class NMSImpl implements NMSBridge {
|
|||||||
public float getSpeedFor(NPC npc) {
|
public float getSpeedFor(NPC npc) {
|
||||||
if (!npc.isSpawned() || !(npc.getEntity() instanceof org.bukkit.entity.LivingEntity))
|
if (!npc.isSpawned() || !(npc.getEntity() instanceof org.bukkit.entity.LivingEntity))
|
||||||
return DEFAULT_SPEED;
|
return DEFAULT_SPEED;
|
||||||
LivingEntity handle = NMSImpl.getHandle((org.bukkit.entity.LivingEntity) npc.getEntity());
|
LivingEntity handle = getHandle((org.bukkit.entity.LivingEntity) npc.getEntity());
|
||||||
if (handle == null) {
|
if (handle == null) {
|
||||||
|
return DEFAULT_SPEED;
|
||||||
}
|
}
|
||||||
return DEFAULT_SPEED;
|
return (float) handle.getAttributeBaseValue(Attributes.MOVEMENT_SPEED);
|
||||||
// return (float) handle.getAttribute(Attributes.d).getValue();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -711,12 +711,11 @@ public class NMSImpl implements NMSBridge {
|
|||||||
public float getSpeedFor(NPC npc) {
|
public float getSpeedFor(NPC npc) {
|
||||||
if (!npc.isSpawned() || !(npc.getEntity() instanceof org.bukkit.entity.LivingEntity))
|
if (!npc.isSpawned() || !(npc.getEntity() instanceof org.bukkit.entity.LivingEntity))
|
||||||
return DEFAULT_SPEED;
|
return DEFAULT_SPEED;
|
||||||
LivingEntity handle = NMSImpl.getHandle((org.bukkit.entity.LivingEntity) npc.getEntity());
|
LivingEntity handle = getHandle((org.bukkit.entity.LivingEntity) npc.getEntity());
|
||||||
if (handle == null) {
|
if (handle == null) {
|
||||||
|
return DEFAULT_SPEED;
|
||||||
}
|
}
|
||||||
return DEFAULT_SPEED;
|
return (float) handle.getAttributeBaseValue(Attributes.MOVEMENT_SPEED);
|
||||||
// return (float)
|
|
||||||
// handle.getAttribute(Attributes.d).getValue();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -762,10 +762,9 @@ public class NMSImpl implements NMSBridge {
|
|||||||
return DEFAULT_SPEED;
|
return DEFAULT_SPEED;
|
||||||
LivingEntity handle = getHandle((org.bukkit.entity.LivingEntity) npc.getEntity());
|
LivingEntity handle = getHandle((org.bukkit.entity.LivingEntity) npc.getEntity());
|
||||||
if (handle == null) {
|
if (handle == null) {
|
||||||
|
return DEFAULT_SPEED;
|
||||||
}
|
}
|
||||||
return DEFAULT_SPEED;
|
return (float) handle.getAttributeBaseValue(Attributes.MOVEMENT_SPEED);
|
||||||
// return (float)
|
|
||||||
// handle.getAttribute(Attributes.d).getValue();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -791,10 +791,9 @@ public class NMSImpl implements NMSBridge {
|
|||||||
return DEFAULT_SPEED;
|
return DEFAULT_SPEED;
|
||||||
LivingEntity handle = getHandle((org.bukkit.entity.LivingEntity) npc.getEntity());
|
LivingEntity handle = getHandle((org.bukkit.entity.LivingEntity) npc.getEntity());
|
||||||
if (handle == null) {
|
if (handle == null) {
|
||||||
|
return DEFAULT_SPEED;
|
||||||
}
|
}
|
||||||
return DEFAULT_SPEED;
|
return (float) handle.getAttributeBaseValue(Attributes.MOVEMENT_SPEED);
|
||||||
// return (float)
|
|
||||||
// handle.getAttribute(Attributes.d).getValue();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -60,7 +60,6 @@ public class AllayController extends MobEntityController {
|
|||||||
|
|
||||||
public static class EntityAllayNPC extends Allay implements NPCHolder {
|
public static class EntityAllayNPC extends Allay implements NPCHolder {
|
||||||
private final CitizensNPC npc;
|
private final CitizensNPC npc;
|
||||||
|
|
||||||
private int taskId = -1;
|
private int taskId = -1;
|
||||||
|
|
||||||
public EntityAllayNPC(EntityType<? extends Allay> types, Level level) {
|
public EntityAllayNPC(EntityType<? extends Allay> types, Level level) {
|
||||||
|
@ -8,10 +8,12 @@ import java.util.Map.Entry;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import com.google.common.collect.BiMap;
|
import com.google.common.collect.BiMap;
|
||||||
import com.google.common.collect.HashBiMap;
|
import com.google.common.collect.HashBiMap;
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
|
import com.mojang.serialization.Codec;
|
||||||
import com.mojang.serialization.Lifecycle;
|
import com.mojang.serialization.Lifecycle;
|
||||||
|
|
||||||
import net.citizensnpcs.util.NMS;
|
import net.citizensnpcs.util.NMS;
|
||||||
@ -20,12 +22,16 @@ import net.minecraft.core.DefaultedRegistry;
|
|||||||
import net.minecraft.core.Holder;
|
import net.minecraft.core.Holder;
|
||||||
import net.minecraft.core.Holder.Reference;
|
import net.minecraft.core.Holder.Reference;
|
||||||
import net.minecraft.core.HolderGetter;
|
import net.minecraft.core.HolderGetter;
|
||||||
|
import net.minecraft.core.HolderOwner;
|
||||||
|
import net.minecraft.core.HolderSet.Named;
|
||||||
import net.minecraft.core.IdMap;
|
import net.minecraft.core.IdMap;
|
||||||
import net.minecraft.core.MappedRegistry;
|
import net.minecraft.core.MappedRegistry;
|
||||||
|
import net.minecraft.core.RegistrationInfo;
|
||||||
import net.minecraft.core.Registry;
|
import net.minecraft.core.Registry;
|
||||||
import net.minecraft.resources.ResourceKey;
|
import net.minecraft.resources.ResourceKey;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.tags.TagKey;
|
import net.minecraft.tags.TagKey;
|
||||||
|
import net.minecraft.tags.TagLoader.LoadResult;
|
||||||
import net.minecraft.util.RandomSource;
|
import net.minecraft.util.RandomSource;
|
||||||
import net.minecraft.world.entity.EntityType;
|
import net.minecraft.world.entity.EntityType;
|
||||||
|
|
||||||
@ -63,22 +69,32 @@ public class CustomEntityRegistry extends DefaultedMappedRegistry<EntityType<?>>
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public EntityType byId(int var0) {
|
public EntityType byId(int var0) {
|
||||||
return this.wrapped.byId(var0);
|
return wrapped.byId(var0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public EntityType byIdOrThrow(int var0) {
|
public EntityType byIdOrThrow(int var0) {
|
||||||
return this.wrapped.byIdOrThrow(var0);
|
return wrapped.byIdOrThrow(var0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canSerializeIn(HolderOwner<EntityType<?>> owner) {
|
||||||
|
return wrapped.canSerializeIn(owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean containsKey(ResourceKey<EntityType<?>> var0) {
|
public boolean containsKey(ResourceKey<EntityType<?>> var0) {
|
||||||
return this.wrapped.containsKey(var0);
|
return wrapped.containsKey(var0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean containsKey(ResourceLocation var0) {
|
public boolean containsKey(ResourceLocation var0) {
|
||||||
return this.wrapped.containsKey(var0);
|
return wrapped.containsKey(var0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Reference<EntityType<?>> createIntrusiveHolder(EntityType<?> type) {
|
||||||
|
return wrapped.createIntrusiveHolder(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -91,6 +107,11 @@ public class CustomEntityRegistry extends DefaultedMappedRegistry<EntityType<?>>
|
|||||||
return wrapped.entrySet();
|
return wrapped.entrySet();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Registry<EntityType<?>> freeze() {
|
||||||
|
return wrapped.freeze();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MappedRegistry<EntityType<?>> get() {
|
public MappedRegistry<EntityType<?>> get() {
|
||||||
return wrapped;
|
return wrapped;
|
||||||
@ -109,16 +130,24 @@ public class CustomEntityRegistry extends DefaultedMappedRegistry<EntityType<?>>
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getId(EntityType key) {
|
public Optional<Named<EntityType<?>>> get(TagKey<EntityType<?>> key) {
|
||||||
|
return wrapped.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<Reference<EntityType<?>>> getAny() {
|
||||||
|
return wrapped.getAny();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getId(EntityType<?> key) {
|
||||||
if (entityIds.containsKey(key))
|
if (entityIds.containsKey(key))
|
||||||
return entityIds.get(key);
|
return entityIds.get(key);
|
||||||
return wrapped.getId(key);
|
return wrapped.getId(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ResourceLocation getKey(EntityType value) {
|
public ResourceLocation getKey(EntityType<?> value) {
|
||||||
if (entityClasses.containsKey(value))
|
|
||||||
return entityClasses.get(value);
|
|
||||||
return wrapped.getKey(value);
|
return wrapped.getKey(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -144,6 +173,11 @@ public class CustomEntityRegistry extends DefaultedMappedRegistry<EntityType<?>>
|
|||||||
return wrapped.getResourceKey(var0);
|
return wrapped.getResourceKey(var0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Stream<Named<EntityType<?>>> getTags() {
|
||||||
|
return wrapped.getTags();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public EntityType<?> getValue(ResourceKey<EntityType<?>> key) {
|
public EntityType<?> getValue(ResourceKey<EntityType<?>> key) {
|
||||||
return wrapped.getValue(key);
|
return wrapped.getValue(key);
|
||||||
@ -156,6 +190,11 @@ public class CustomEntityRegistry extends DefaultedMappedRegistry<EntityType<?>>
|
|||||||
return wrapped.getValue(key);
|
return wrapped.getValue(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Codec<Holder<EntityType<?>>> holderByNameCodec() {
|
||||||
|
return wrapped.holderByNameCodec();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEmpty() {
|
public boolean isEmpty() {
|
||||||
return wrapped.isEmpty();
|
return wrapped.isEmpty();
|
||||||
@ -166,16 +205,41 @@ public class CustomEntityRegistry extends DefaultedMappedRegistry<EntityType<?>>
|
|||||||
return wrapped.iterator();
|
return wrapped.iterator();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResourceKey<? extends Registry<EntityType<?>>> key() {
|
||||||
|
return wrapped.key();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<ResourceLocation> keySet() {
|
public Set<ResourceLocation> keySet() {
|
||||||
return wrapped.keySet();
|
return wrapped.keySet();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Stream<Reference<EntityType<?>>> listElements() {
|
||||||
|
return wrapped.listElements();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Stream<Named<EntityType<?>>> listTags() {
|
||||||
|
return wrapped.listTags();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PendingTags<EntityType<?>> prepareTagReload(LoadResult<EntityType<?>> var0) {
|
||||||
|
return wrapped.prepareTagReload(var0);
|
||||||
|
}
|
||||||
|
|
||||||
public void put(int entityId, ResourceLocation key, EntityType entityClass) {
|
public void put(int entityId, ResourceLocation key, EntityType entityClass) {
|
||||||
entities.put(key, entityClass.builtInRegistryHolder());
|
entities.put(key, entityClass.builtInRegistryHolder());
|
||||||
entityIds.put(entityClass, entityId);
|
entityIds.put(entityClass, entityId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<RegistrationInfo> registrationInfo(ResourceKey<EntityType<?>> key) {
|
||||||
|
return wrapped.registrationInfo(key);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<ResourceKey<EntityType<?>>> registryKeySet() {
|
public Set<ResourceKey<EntityType<?>>> registryKeySet() {
|
||||||
return wrapped.registryKeySet();
|
return wrapped.registryKeySet();
|
||||||
@ -191,6 +255,16 @@ public class CustomEntityRegistry extends DefaultedMappedRegistry<EntityType<?>>
|
|||||||
return wrapped.size();
|
return wrapped.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return wrapped.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Holder<EntityType<?>> wrapAsHolder(EntityType<?> type) {
|
||||||
|
return wrapped.wrapAsHolder(type);
|
||||||
|
}
|
||||||
|
|
||||||
private static final MethodHandle IREGISTRY_LIFECYCLE = NMS.getFirstGetter(MappedRegistry.class, Lifecycle.class);
|
private static final MethodHandle IREGISTRY_LIFECYCLE = NMS.getFirstGetter(MappedRegistry.class, Lifecycle.class);
|
||||||
private static final MethodHandle IREGISTRY_RESOURCE_KEY = NMS.getFirstGetter(MappedRegistry.class,
|
private static final MethodHandle IREGISTRY_RESOURCE_KEY = NMS.getFirstGetter(MappedRegistry.class,
|
||||||
ResourceKey.class);
|
ResourceKey.class);
|
||||||
|
@ -779,10 +779,9 @@ public class NMSImpl implements NMSBridge {
|
|||||||
return DEFAULT_SPEED;
|
return DEFAULT_SPEED;
|
||||||
LivingEntity handle = getHandle((org.bukkit.entity.LivingEntity) npc.getEntity());
|
LivingEntity handle = getHandle((org.bukkit.entity.LivingEntity) npc.getEntity());
|
||||||
if (handle == null) {
|
if (handle == null) {
|
||||||
|
return DEFAULT_SPEED;
|
||||||
}
|
}
|
||||||
return DEFAULT_SPEED;
|
return (float) handle.getAttributeBaseValue(Attributes.MOVEMENT_SPEED);
|
||||||
// return (float)
|
|
||||||
// handle.getAttribute(Attributes.d).getValue();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -533,10 +533,9 @@ public class NMSImpl implements NMSBridge {
|
|||||||
return DEFAULT_SPEED;
|
return DEFAULT_SPEED;
|
||||||
EntityLiving handle = NMSImpl.getHandle((LivingEntity) npc.getEntity());
|
EntityLiving handle = NMSImpl.getHandle((LivingEntity) npc.getEntity());
|
||||||
if (handle == null) {
|
if (handle == null) {
|
||||||
|
return DEFAULT_SPEED;
|
||||||
}
|
}
|
||||||
return DEFAULT_SPEED;
|
return (float) handle.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED).getValue();
|
||||||
// return (float)
|
|
||||||
// handle.getAttributeInstance(GenericAttributes.d).getValue();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
Loading…
Reference in New Issue
Block a user