Jump to content

jabelar

Members
  • Posts

    3266
  • Joined

  • Last visited

  • Days Won

    39

Everything posted by jabelar

  1. Player movement is generally controlled on the client side. The best way for making automatic player movement seems to be to replace the client side mouse helper class with one of your own and then stimulate mouse and keyboard activity.
  2. In older versions I would grab the list client side and send packet to server during the post init loading stage. Useful for things like generating random drops.
  3. If you want a lot of control to change a gui, you can replace the GUI with one of your own that basically copies the original code but with the modifications you want. Then handle the gui open event and if the gui is the one you want to replace, replace it.
  4. The way he does the static initializer block should be fine. Universal bucket works for me and I enable it the same way. You can try by looking/using/copying my example mod here: https://github.com/jabelar/ExampleMod-1.12 Perhaps the problem has to do with the fluid, or the order in which the fluid and bucket are instantiated or registered. But your code looks like it should work. The only thing you do that is kinda weird/different than most is that you instantiate an anonymous instance of your loader classes whereas I would normally make the loader methods static. I don't think that's really a problem, but maybe it is causing trouble.
  5. Yeah, it sounds confusing because I'm talking about build time when there are two "build time" points. But my point was that @Side.CLIENT "vanilla" classes are not even in the server JAR (when they are built) so they are fully inaccessible (can't be loaded) and therefore if you extend them you also need to annotate so at ForgeGradle build time things work out. Is that correct? Or do the mod side-annotated classes exist in the JAR and it gets sorted out during class loading or run-time?
  6. If the functionality really is separate or not dependent, then yeah it can make sense to make them separate mods. It's really a style thing, with pros and cons. I would organize it based on the way the users will think about it. Will they be thinking that it is essentially the same mod or will they think of it as separate functionality? Would anyone ever want to install the server side without the client? If not, I would probably let the sided annotation do its job -- the server JAR will be nice and compact without making it a separate mod.
  7. The annotation culls out the annotated stuff at build time. So the server JAR won't include anything annotated that way. Your client-side code is probably accessing Side.CLIENT annotated vanilla classes (rendering and such) so probably a lot of your client-side mod code will need to be similarly annotated -- if you have call any sided code, then your code that calls it has to also be sided. So it will mostly work itself out. You will have to annotate some of your stuff and that won't be included in the server JAR when you build your mod. You can go further and annotate other things that you're sure are on a single side, but many people say you shouldn't (I don't really understand the concern). I would not create two separate mods though (one client-side and one server-side) if they are needed to work together. That would be annoying and confusing to people using it as Draco pointed out.
  8. Okay, back to your original question. So both the server and client are totally separate threads. The only issue with mixing up sides is if you try to access classes that aren't supposed to be available (but might be if you're using integrated server) due to being on the other "side". So methods are only called in the client thread from the client thread. And methods are only called in the server thread from the server thread. However, there are some methods which are part of the logic on both sides and will both get called (separately but in parallel). This is for the reason I explained above -- some logic does need to execute on the client in order to smooth out the action between server updates. However, the client calls it itself. The client is just trying to copy what the server will do. It is actually kinda hard by just looking at the code to know where the code will run. There is very likely code that is not marked as SideOnly that still only runs on one side. And if the code has any checks for world#isRemote then it will execute differently depending on where it will run. Remember, the Side.CLIENT stuff is stripped out to optimize the server JAR size but there is no requirement to strip out everything. In many ways you just need to get a feel for which things are working in each place. One good way to get a feel for that is to make some test classes (block, entity, etc.) that have console statements in the methods of interest and watch what get's printed to the console. You'll see some that happen in one thread or the other, and some that happen in both. Sometimes things are obvious, like a renderer only being on client side. However, sometimes it gets confusing because the server implementation will use a same or similar method name to notify clients (rather than do the client thing) -- a good example is various sound playing methods where on the client they will directly play the sound, but on server they will send notifications to the clients (to tell them to play the sound). So don't feel too bad if you still get confused. But do try to understand the main concepts: 1) The dedicated server JAR does not have access to anything marked Side.CLIENT, although testing with integrated server will fool you (because it does have access). 2) Methods that are not marked as sided may run on one side or both, and if they run on both may use world#isRemote to have different behavior. P.S. Modders mostly run into problems with Side.CLIENT., not Side.SERVER There are also cases of Side.SERVER but those are mostly limited to networking, user authentication, and a few other MinecraftServer fields, so modders don't seem to run into problems with that much.
  9. Okay, let me give you a high-level explanation of this whole "sides" stuff. Whenever you make a client and server game you don't want people to be able to cheat. Therefore you want all game logic to happen on the server, otherwise people would create clients that would do things like allow them to teleport or do extra damage and such. So ideally every multi-player game would have all logic on the server and the client would only do user interface (graphics, sounds and input from keyboard and mouse). However, if you try to make a game where the client only does user interface you'll quickly find that the movement is not smooth. This is because the graphics updates faster than the network can send the information from the server. So every real-time game has to figure out a way where the client does some of the processing in between updates from the server. Unfortunately, because the game is complicated for the client to smooth things out graphically it needs to actually process some of the logic while waiting for updates. That is the general idea. But then in Minecraft there are a couple specific additional things that cause confusion. In order to optimize things like memory use, they decided it would be good to not load things that are not needed. So they used the "sided" annotation. When a class or field or method is annotated as Side.CLIENT then the server doesn't have to load it and saves memory. That would be easy to understand except there is one other thing. Normally your client and server will run in separate Java machine instances. However, that is a bit wasteful when playing in single player. So instead they decided to combine them into a single Java instance (integrated server). The problem with the integrated server is that because it is a single Java machine running both sides then the classes for both sides are loaded -- basically the sided annotation is pretty much ignored. Yes there are two threads, but they are in one JVM. This causes issues for novice modders because if they write code that "reaches across the sides" it will still appear to function without error. A lot of modders will test in integrated server mode and so won't realize the problem and then get yelled at when they post their code on the forum. So a very important tip for modding is always test your mod by running with a dedicated server. You can do this easily in Eclipse by first running the Server run configuration and then while that is running run the Client run configuration. Then in your client game you choose connect to server and put in the "loopback" address 127:0:0:1. If you test like this, you'll very quickly learn when you are making mistakes with sided functionality.
  10. To be honest, the division between the logic running on each side can be a bit murky. In this case the LivingAttackEvent may still have some use on the client side but it admittedly is less useful due to the much less information about the damage source. Overall though processing the event on the client side would only be useful for visual or audio effects, since any lasting game logic (such as modifying the actual attack) has to happen on the server.
  11. The first paragraph is the way it actually works. I'm not suggesting something. I'm describing the Minecraft actual design. The mod has to be client-side because it isn't our server and so we have no ability to add a mod to it. This is for multi-player servers. Like if you wanted to join a MinePlex server. How would you possibly get them to put your mod on their server? That makes no sense.
  12. So the way it works is that when the server processes an attack it updates the client with an SPacketEntityStatus packet. Unfortunately, it is implemented in a very minimalistic way, presumably for performance reasons. So there is a single byte value sent for the status. The codes are handled on the client side by the Entity#handleStatusUpdate() method. Since only a byte is transferred, only a few special types of damage are called out based on the need for client effects. For example, DamageSource.FIRE, DROWN and ENCHANT_THORNS_HIT have distinct codes and all other types of damage are considered DamageSource.GENERIC (and this is instantiated on the client side so no additional information like the attacking entity is included). So without a server-side mod it is very difficult to figure things out on the client side. I don't think it is entirely impossible to figure it out or at least make a good guess. For example, you could probably try to detect colliding entities and look at where they are in their attack sequence and such. But it would be a lot of work and probably not perfectly accurate in cases where lots of things are simultaneously happening. What could be done is that you could create a pull request on MinecraftForge to add a packet type where actual damage source is transferred and processed separately from entity status updates. Since damage is a relatively rare event I think having a more verbose packet would not hurt. If you figured out a solid way to implement it and that got accepted then future versions of Forge would support client-only damage interpretation. Since it is admittedly a useful thing to know for modding, it might get accepted.
  13. @Differentiation, you're missing how mods are used. It is possible for people to make mods that only get applied to one side. For example, if I wanted to have my client render everything in pink for some reason there is no reason for the server to apply the mod. In the @Mod annotation you can indicate that it doesn't need to be on both side. Or on the server side you might want to change the way saving is handled, which the client wouldn't need to know about. The ability to make client-only mods is important because you wouldn't want to make a server administrator have to apply every mod that every player might want on their client. Now in most cases what you can do on one side is extremely limited, so yes in many cases (including the one discussed in this thread) both the server and client need to be involved.
  14. The MinecraftForge project has an official formatter template which I recommend that you download and put in a text file in your workspace then import it with Window | Preferences | Java | Code Style | Formatter | Import... Also you can choose whether it applies automatically or manually. If you choose to apply it manually, then you can do so by selecting the file in the Project Explorer window and right-clicking and select Source | Format. Or in general you should go through the formatter settings yourself and select things like using spaces for tabs.
  15. If what you're trying to render is related to a block, tileentity, item or entity, then you would make an appropriate model and textures. However, it sounds like you want to do more of an "overlay" / HUD, meaning a visual guide for a player that isn't exactly any particular object? If the overlay is simple (like a 2D set of lines, or even 3D that is on top of everything else) then you can simply handle the RenderGameOverlay event. But I suspect you want something fancier where the lines have perspective and actually follow the terrain? The problem in that case is the order of rendering as some parts of your line would be behind other things in view. Is that what you want? Maybe if you drew an illustration it would better help those who try to advise you.
  16. I didn't know it was cyan because I use black and white on my monitor for eye health reasons. But yeah, I can try to remember to remove formatting/color.
  17. I didn't do that on purpose. I cut and pasted from a different forum.
  18. I'm not entirely sure but you might be able to qualify the NBT by treating the ingredient at a conditional ingredient. If you want conditional ingredients, you'll need to create an IIngredientFactory and specify it in _factories.json. In the IIngredientFactory, you can use CraftingHelper.processConditions to check if the conditions are met. If they are, return an Ingredient instance for the specified item; if they aren't, return Ingredient.EMPTY (an Ingredient that never matches any ItemStack). You can see an example implementation by Choonster here. Alternatively, maybe you need to make a custom IRecipe.
  19. Actually, the way to do this is to override the addCollisionBoxToList() method. In the past I have made the collision box change based on the entity that was colliding with it. The cool thing about this method is it passes the entity. I guess you could look at the entity motion and compare with the facing direction to declare a collision (and then have the collision do damage). EDIT: Just saw you posted while I was writing this and looks like you indeed did use that method. Cool.
  20. If you're going to take a different approach, what about considering the ReplaceBiomeBlocks event? That event gets the world so you could try to access the biomes array and let the normal generation continue with the revised biomes. However, I just tried and it is tough because you'd need to use the following crazy code event.getWorld().getBiomeProvider().getBiomes(event.getWorld().getWorldInfo().getTerrainType().getChunkGenerator().biomesForGeneration, event.getX() * 16, event.getZ() * 16, 16, 16) and the biomesForGeneration is private so you'd have to use Java reflection. I think I'll put a pull request in to have the event updated to provide the biomes array as well, so in the future this will be easier. Alternatively, you could copy the generation code (based on world type) into your event handler since the event also gets the ChunkPrimer and cancel the vanilla generation. Doing it this way should solve any problems with architectural issues on how you hook into the generation. After that it really depends on how efficient your algorithm is for coming up with the replacement biomes. If you still have memory problems at that point, it must be your algorithm.
  21. It depends on exactly what you mean in terms of how it would look. But if it would look like a slab with some pointy sticks on it, then if you make the bounding box just to cover the slab portion then it should work pretty well. I can't think of an easy where where the collision block changes based on the way the player enters though. The getCollisionBox() method only takes in information about the block, not about whatever is trying to process the collision box. So it is probably best to not make it dynamic, but instead make a fixed slab that is part of the block.
  22. If you look at where GUIs are opened in the Minecraft#displayGuiScreen() method, you'll see that as soon as the current GUI is set to a new GUI (i.e. just opened the GUI) the following code is run: ScaledResolution scaledresolution = new ScaledResolution(this); int i = scaledresolution.getScaledWidth(); int j = scaledresolution.getScaledHeight(); guiScreenIn.setWorldAndResolution(this, i, j); Since that code is running in the Minecraft class, the "this" refers to the entire game window. So you can see that the idea is that the GuiScreen "resolution" is set to be the entire game window. In other words, the GuiScreen always covers the whole window, but it is up to you where you draw your Gui within the game window. If you look at the GuiScreen#setWorldAndResolution() method you'll see that it sets the height and width fields of the GuiScreen. So with all that background you should be able to understand how to scale your Gui. You should leave the height and width fields, but do drawing locations based on them. So if you wanted to draw in the middle of the game window you'd use height/2 and width/2. This will ensure they scale along with the minecraft game window. Hope that makes sense. Also, just look at other GuiScreen-based classes to understand how they position things.
  23. the JSON system is considered the proper modern way to do it. One advantage is that it will be able to be replaced in resource packs rather than "hard coded" into your mod. Unless you're doing something very unusual with recipes (in which case you might implement your own IRecipe class) you should use the JSON along with the registry event system.
  24. Containers are convenient if you want to sync data between the client and server, and particularly if there is an inventory. Technically though you can send a custom packet to keep the client up to date and send back interaction information and make a simpler custom GUI. I've done the latter before and it isn't that hard.
  25. There are events for FluidFillingEvent and FluidDrainingEvent. Of course since fluids are for mods, these events will only fire if the modder uses the suggested fluid tank implementation but this is likely.
×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.