When the game is paused in SSP world, speakers are not ticked. However,
System.nanoTime() continues to increase, which means the next tick
speakers believe there has been a big jump and so schedule a bunch of
extra audio.
To avoid this, we keep track of how long the game has been paused offset
nanoTime by that amount.
Fixes#994
It's just more confusing having to keep track of where the ByteBuffer is
at. In this case, I think we were forgetting to rewind after computing
the digest.
Hopefully we'll be able to drop some of these in 1.17 as Java 16 has
a few more ByteBuffer methods
Fixes#992
Speakers can now play arbitrary PCM audio, sampled at 48kHz and with a
resolution of 8 bits. Programs can build up buffers of audio locally,
play it using `speaker.playAudio`, where it is encoded to DFPWM, sent
across the network, decoded, and played on the client.
`speaker.playAudio` may return false when a chunk of audio has been
submitted but not yet sent to the client. In this case, the program
should wait for a speaker_audio_empty event and try again, repeating
until it works.
While the API is a little odd, this gives us fantastic flexibility (we
can play arbitrary streams of audio) while still being resilient in the
presence of server lag (either TPS or on the computer thread).
Some other notes:
- There is a significant buffer on both the client and server, which
means that sound take several seconds to finish after playing has
started. One can force it to be stopped playing with the new
`speaker.stop` call.
- This also adds a `cc.audio.dfpwm` module, which allows encoding and
decoding DFPWM1a audio files.
- I spent so long writing the documentation for this. Who knows if it'll
be helpful!
- Remove all the hungrarian notation in variables. Currently leaving
the format of rednet messages for now, while I work out whether this
counts as part of the public API or not.
- Fix the "repeat" program failing with broadcast packets. This was
introduced in #900, but I don't think anybody noticed. Will be more
relevant when #955 is implemented though.
This means that if the current player is breaking a cable/wired modem,
only the part they're looking at has breaking progress. Closes#355.
A mixin is definitely not the cleanest way to do this. There's a couple
of alternatives:
- CodeChickenLib's approach of overriding the BlockRendererDispatcher
instance with a delegating subclasss. One mod doing this is fine,
several is Not Great.o
- Adding a PR to Forge: I started this, and it's definitely the ideal
solution, but any event for this would have a ton of fields and just
ended up looking super ugly.
I assume people have broken coroutine dispatchers - I didn't think it
was possible to queue an actual event with no type.
See cc-tweaked/cc-restitched#31. Will fix it too once merged downstream!
testModExtra must /strictly/ be the set of dependencies which are not
present in implementation - there can't be any duplicates.
Yes, it's stupid, but the whole lazyToken("minecraft_classpath") thing
wasn't really built with this in mind, so not much we can do :)
Opening a screen KeyBinding.releaseAll(), which forces all inputs to be
considered released. However, our init() function then calls
grabMouse(), which calls Keybinding.setAll(), undoing this work.
The fix we're going for here is to call releaseAll() one more time[^1]
after grabbing the mouse. I think if this becomes any more of a problem,
we should roll our own grabMouse which _doesn't_ implement any specific
behaviour.
Fixes#975
[^1]: Obvious problem here is that we do minecraft.screen=xyz rather
than setScreen. We need to - otherwise we'd just hit a stack
overflow - but it's not great.
- Build fails right now due to module issues, so this won't be pushed
to GitHub.
- Monitors render transparently when loaded into the world. I don't
think this is a 1.17 bug, so not sure what's going on here!
Peripherals can now have multiple types:
- A single primary type. This is the same as the current idea of a
type - some identifier which (mostly) uniquely identifies this kind
of peripheral. For instance, "speaker" or "minecraft:chest".
- 0 or more "additional" types. These are more like traits, and
describe what other behaviour the peripheral has - is it an
inventory? Does it supply additional peripherals (like a wired
modem)?.
This is mostly intended for the generic peripheral system, but it might
prove useful elsewhere too - we'll have to see!
- peripheral.getType (and modem.getTypeRemote) now returns 1 or more
values, rather than exactly one.
- Add a new peripheral.hasType (and modem.hasTypeRemote) function which
determines if a peripheral has the given type (primary or
additional).
- Change peripheral.find and all internal peripheral methods to use
peripheral.hasType instead.
- Update the peripherals program to show all types
This effectively allows you to do things like
`peripheral.find("inventory")` to find all inventories.
This also rewrites the introduction to the peripheral API, hopefully
making it a little more useful.
- Capability invalidation and tile/block entity changes set a dirty bit
instead of refetching the peripheral immediately.
- Then on the block's tick we recompute the peripheral if the dirty bit
is set.
Fixes#696 and probably fixes#882. Some way towards #893, but not
everything yet.
This is probably going to break things horribly. Let's find out!
- Bump copy-cat version to have support for initial files in
directories and the blit fixes.
- Add an example nft image and move example nfp into a data/ directory.
- Fix nft parser not resetting colours on the start of each line.
The feature nobody asked for, but we're getting anyway.
Old way to register a turtle/pocket computer upgrade:
ComputerCraftAPI.registerTurtleUpgrade(new MyUpgrade(new ResourceLocation("my_mod", "my_upgrade")));
New way to register a turtle/pocket computer upgrade:
First, define a serialiser for your turtle upgrade type:
static final DeferredRegister<TurtleUpgradeSerialiser<?>> SERIALISERS = DeferredRegister.create( TurtleUpgradeSerialiser.TYPE, "my_mod" );
public static final RegistryObject<TurtleUpgradeSerialiser<MyUpgrade>> MY_UPGRADE =
SERIALISERS.register( "my_upgrade", () -> TurtleUpgradeSerialiser.simple( MyUpgrade::new ) );
SERIALISERS.register(bus); // Call in your mod constructor.
Now either create a JSON string or use a data generator to register your upgrades:
class TurtleDataGenerator extends TurtleUpgradeDataProvider {
@Override
protected void addUpgrades( @Nonnull Consumer<Upgrade<TurtleUpgradeSerialiser<?>>> addUpgrade )
simple(new ResourceLocation("my_mod", my_upgrade"), MY_UPGRADE.get()).add(addUpgrade);
}
}
See much better! In all seriousness, this does offer some benefits,
namely that it's now possible to overwrite or create upgrades via
datapacks.
Actual changes:
- Remove ComputerCraftAPI.register{Turtle,Pocket}Upgrade functions.
- Instead add {Turtle,Pocket}UpgradeSerialiser classes, which are used
to load upgrades from JSON files in datapacks, and then read/write
them to network packets (much like recipe serialisers).
- The upgrade registries now subscribe to datapack reload events. They
find all JSON files in the
data/$mod_id/computercraft/{turtle,pocket}_upgrades directories,
parse them, and then register them as upgrades.
Once datapacks have fully reloaded, these upgrades are then sent over
the network to the client.
- Add data generators for turtle and pocket computer upgrades, to make
the creation of JSON files a bit easier.
- Port all of CC:T's upgrades over to use the new system.
- Subscribe to the "on add reload listener" event, otherwise we don't
get reloads beyond the first one! This means we no longer need to
cast the resource manager to a reloadable one.
- Change the mount cache so it's keyed on path, rather than "path ✕
manager".
- Update the reload listener just to use the mount cache, rather than
having its own separate list. I really don't understand what I was
thinking before.