[Music]
hello everybody my name is alvarius I'm
an onchain developer mostly focusing on
fully onchain games and today I'm going
to tell you a story about how we built
our first fully onchain game and made a
big mess and then build a framework that
now allows us to build full onchain game
prototypes in a matter of a couple of
days but first let me take you back to
around three and a half years ago um
three and a half years ago my friend
Justin and I we discovered Dark Forest
and we got really inspired by it dark
forest was one of the first fully
onchain games and that means basically
all the game mechanics all the physics
of your game are deployed or are living
in a um in smart contracts on a public
blockchain and then the client is
essentially just a window in to this
onchain world and now you suddenly have
something where um the world will
essentially live on as long as the chain
will live on which makes it feel very
real and then also the other thing is
you're deploying to a public Global
computer where also everybody else can
deploy to the same computer and extend
the world so there's a lot of potential
for um emergent Behavior here and we had
this feeling that we there's this
potential to create something that can
be more meaningful than existing Virtual
Worlds of course just a potential you
still have to build a fun game and so we
got started
building we get started with the default
patterns that everybody was using at the
time so the first thing we wanted to add
was positions for our entities in the
game and so we added a mapping on the
contract and then also a mapping on the
client because of course we want to
render the entities the position of our
entities in our
client and then we had to add a couple
of getter functions so that our client
when the Cent loads can basically
hydrate the initial State on the
client and then we added the first
physics of our game the move function um
and in here we have to make sure to both
modify the state but then also emit an
event that the client can listen to to
keep the state in sync so this is a lot
of boilerplate just for having um one
type of State synchronized between
contracts and clients and of course we
don't only want position but but a lot
more on chain state so the next thing we
added was health our entities but now we
had to add the exact same baller plate
again mappings G functions custom events
custom sync stack on the
client and then the same thing for
energy and for skills and for inventory
and you get the idea it's the same
baller plate over and over again and at
some point we were at the point where we
couldn't really iterate on the actual
game mechanics anymore because every
time we wanted to change anything we
basically had to drill that change
through the entire s stack just to keep
the contract and client state in sync
and so at some point we decided to trash
everything we have and change something
um we decided to build a framework to
solve our own problems so that for the
next project we could basically skip all
the boilerplate and just focus on the
fun stuff on the game mechanics of our
game and that framework over the last
three and a half years turned into mud
and now we're not the only ones using it
now it's used by a bunch of teams um
from small teams to Big teams who all
ran into the same problems that we ran
into and now they use muds to ship
faster so in the next 15 minutes I'm
going to talk about three problems that
we ran into back then and how we solved
them in
mud the first one I already told you
about onchain games have a lot of state
and you need to synchronize that state
to the client to render a game and I
also already told you about the fact
that the default approach of using gter
functions events and so on doesn't scale
well
and so here's what we did the first
thing we did was standardize the way we
basically shape our our state and so now
we just use tables for everything um a
table you can kind of think of it as a
mapping in solidity you can implement it
as a mapping in solidity um but we like
to think of it as tables and tables can
have multiple rows we call those records
and then each record is uniquely
identified by one or more keys and now
the important thing is what happens when
a record in our table changes
well instead of emitting a different
event for each different type of table
like position changed Health changed and
so on WE emit one default event one
generic event for all the tables and on
that event we have enough information um
to know which table changed which record
changed and which value in that record
changed and then with this generic event
we can then build a syn stack that
understands this generic event and then
takes care of updating the same value on
the client
and now we don't need any you know we
don't need a custom syn stack adapter
for every different type of table but we
can reuse the same code for all the
different state that we have and then we
generate some libraries for the
contracts that basically just take care
of setting the state and then also
emitting this event so we don't have to
think about that every time and now we
essentially eliminated all the boiler
plate because now adding a new type of
state is just one line of code on the
contracts it's automatically synced to
the client and then one line on the cont
on the client to access the state
there and then once we have this
automatic syn stck we can do more fun
stuff with it like we can also pipe the
state into a relational database from
which then the clients can use it to
sync the state quicker so we now also
have an automatic indexer and because
now all the state lives in an in a
relational database we can also use SQL
to basically query all of our onchain
state now some of you might might have
noticed that solidity doesn't actually
have generic events so we have to use a
little trick here what we do is on the
contracts we basically encode the state
as tightly as possible and then um we
store the and then we emit that encoded
data as as bites on the event so our
event signature is just bites everywhere
and then on the contract also we have a
a special table which stores the schema
of all the other tables and this table
is also synced through the through the
default sync stack and that means now
the clients and indexers have the
information that they need to then
decode this state update and all of that
is encapsulated in this automatic sync
stack um so it all happens
automatically um I have a little demo
for how this looks in practice and I
think I have to yep all right so here we
see um a client on the on the left side
we see a default app of M counter every
time we click the button a transaction
is sent on the right side you see the
world Explorer you can see the
transactions and then what happens is in
the background the sync stack basically
synchronizes the state from the
contracts back to the client and that's
when the UI updates and the other thing
you can see here is in the world
Explorer there's also a view to um
basically inspect all the all the data
in your app because it's all
standardized in tables and then we can
look at another quick demo which is um a
slightly more complex table a task list
and here we can use SQL input on the
world Explorer as well um to basically
fill tilter down the task list to only
display the tasks that have been
completed or or not completed yet and
obviously you could also use more
complex SQL
here all right so that's the core of it
but that's it doesn't end here the next
problem we ran into is logic onchain
games also besides a lot of State they
also have a lot of logic so the next
issue we ran into is the you know 24
kilobytes bite code limit on contracts
um here's how we solve it the General
approach is very similar to the diamond
pattern so we have one Central entry
point which we call the world contract
and then into that contract we register
stateless system contracts that
basically implement the functionality
and then when a user calls the W
contract they only need to know the
address of this one world contract and
then the call is forwarded to the system
contract so far pretty standard Diamond
um but now we can add the store to
it in the store we have a table for the
systems so now when we register a new
system in the world information about
the system is also stored in the systems
table and now the automatic syn stack
can take over and basically you know
mirror that information into a database
and then from that database we can
generate an an API which is always up to
date so when we redist a new system that
also gets reded into the systems table
and then the ABI immediately
updates now I told told you that systems
are stateless which is true but they can
still interact with State they can read
from tables and they can also ride to
tables and then that data is also again
automatically synced to the client but
because the system contract itself
doesn't contain any state we can very
easily upgraded it's really trivial we
literally just swap out the
implementation address and we don't have
to um take care of you know storage
slots ordering or whatever because the
system contracts themselves totally
stateless all the state lives in the
store in the world
and then when new tables are registered
at runtime later those are also picked
up by the by the automatic sync stack
immediately um and each table has its
own storage position in this contract so
there's no state overlaps or storage
slots overlaps and with that we can we
can then take it one step further and
introduce the concept of name spaces
which basically means a system that is
registered in a nam space can read from
all the tables but can only write to the
tables that are in the same name space
and with that we can now really lean
into extendability because we can
basically allow anybody to register new
functionality in our world
contract all right another quick demo we
see again the counter table and now we
see the interact tab in the world
Explorer which basically shows all the
functions available on the world
including the one um that increments the
counter and we can also interact with
the world through this uh Explorer and
now when we we add a new function the
another increment function that adds two
to the counter we save that file in the
background it's immediately upgraded
through the def tool um and then the the
new AI function immediately shows up on
the world Explorer and we can again use
the world Explorer to interact with the
app all right last one for today is
higher level
logic you may be familiar with ec20 and
the approve and transfer from
functionality which basically among
other things oh well it allows you to um
give somebody permission to transfer on
your behalf which among other things um
enables Atomic swaps so pretty cool now
let's say we want to add that to our
game we want to allow somebody we want
to give somebody permission to move our
unit um on our behalf um now we could
add an approved move function and a move
from function and then when we want to
do the same for attacking we could add
an approve attack and then attack from
function but that would again be a lot
of voiler plate and now let's say we
wanted to add something very similar but
slightly different um we want to allow
somebody to sign a piece of data that
then allows us to um attack or move on
somebody's behalf so should we add move
with signature attack with signature
because we have a framework now we can
move that logic one level higher to the
level of the framework and then we don't
have to implement it in each of the
systems so in our case we can add a new
entry point to the world which is which
is called call from and it takes another
argument in addition to the system
arguments which is the address that you
want to call on whose behalf you want to
call and then this entry point can
implement the the functionality for
checking that this delegation is
actually valid and then if it's valid it
forwards the call to the system with
this new address that you're calling on
whose behalf you're calling and then
with that we don't have to implement
that for every system again but rather
it just implemented once at the
framework level and works for all the
systems and same with the the signature
example we we have a call with signature
end point entry point here where you can
call it with a signature from somebody
that allows you to call a function on
their behalf and then if it's valid it's
forwarded to the system as them
basically this is useful for a bunch of
different things but one example is
session wallets so in our game we don't
want uh an approval pop up every time we
do anything um oh not not d a time yet
um we don't want an approval popup when
we do anything but rather we want to
create a a scope to delegation once at
the beginning and then um the user
should be able to for the for the
duration of the session to just do moves
in in the game without additionally
approve approving and in our case now we
can connect the main account we can
create the scope delegation through this
entry point function and then um yeah
user doesn't have to click approve every
time again and we have again another
quick quick demo of this um this is
using some UI that ships with with M um
here we the user can create an account
um or connect an existing account in
this example we're creating a pasy
account then a delegation is set up um
and then once that is set up the user
can basically move in the game without
Additionally you know clicking approve
every time
again this is an a demo app we built to
demo this this onboarding flow but then
also some latency stuff that we that we
walked
um you can try it out if you want on
your phone on eat thefly.com
um
and yeah this was just a tiny glimpse
into mud um which we built in the
beginning to solve our own problems but
now is used by many people to who had
the same problems um many of them are
going to present to tomorrow at mday
here in this venue at classroom a um so
I would recommend to to uh come check
that out if this was interesting to you
and then if you can't make it tomorrow
or just in general you can also learn
more about mud on m.de and with that
thank you and hopefully see you
tomorrow good stuff I don't know if I'm
a Frog or if I'm if it's lunchtime
because that made me
hungry cool so the first question uh
from the Q&A is that in your demo
transactions are happening instantly in
redstone block time is two seconds
what's going on
there yes so that's the that's some some
new um instant confirmation stuff we
worked on um we're going to talk more
about that tomorrow at mday um so yeah I
don't want to I don't want to reveal too
much so I would recommend to come to
mday tomorrow very
interesting next question how do you
handle a table growing beyond the size
of one storage
slot okay that that might have been
unclear in the way I explained it tables
can grow indefinitely big um you can
basically one table the the table um can
have multiple records and the records
can span multiple storage slots so
there's no there's no one storage slot
limitation um you can have a the schema
has some limitation in the the number of
fields you can have I think it's 28
Fields um and then one table can have
multiple records as many as you want and
that just you know is is linear in
storage so so you can go on as as as as
much as you want okay so how does Mud
sync table schema changes for Rel
relational databases and ensure that
there are no issues what happen happens
if that schema migration does not work
that's a that's a great question um you
can't currently change a schema so it's
basically you register a table once and
then that's the schema that you will
have um if you if you need to change the
schema you can create a new table um in
theory it should it it would be like
doable to add fields to your schema but
yeah like the with the mentioned
complications you would have to do a
database migration as well and that's um
that's not not trivial so we decided to
just make schema
immutable cool um how were namespaces
implemented in
evm um I don't know into how much detail
I can go here but it's basically just
like contract logic like we um have
access control logic in the wall
contract that makes sure that you can
only um write to tables uh uh that you
that you have access to oh maybe maybe
one thing that I didn't mention that
that might make this more clear um if a
system is registered in a nam space then
what happens if the user calls the the
World entry point is the world doesn't
do a delegate call to the system which a
normal like default Diamond would do um
but rather it does a call and then when
the system wants to read or write from
the table then it basically does a call
back to the world and at that point we
can do access control um on the name
spaces okay I think this is the last
question in the queue uh what do you
think about the possibility of building
real-time multiplayer games using new
existing infrastructure projects that
provide tees trusted execution
environments while using mud to store
the incremental changes to save costs
yeah this is also a great question um
again I would recommend you all to come
to mday Tomorrow there's there's some
demos there um that kind of do something
in this direction it's a real-time game
um slightly different approach but yeah
definitely definitely possible to do
real- Time games at this point this is
the same talk tomorrow right it's uh in
classroom a uh tomorrow I'm not sure
what time exactly I think 1 11: until 5:
or something okay classroom May if you
want to hear about when they do
immediate stuff with
tees okay we got one more question Game
Dev is often on the tech Frontier what
lessons for the do you have for the rest
of the
industry yeah so we started this
framework as a as a way to build our
games more efficiently but as we built
this framework we at some point we
realize there's nothing in this
framework that's specific to to game def
actually um so yeah if you're if you're
building an onchain app that's not a
game I would recommend you to just try
it out as well um and it might just make
it easier okay and I think we got time
for one more question um why do we need
to bring everything on chain and how
does this scale moving forward for FPS
games yeah this is almost a
philosophical question like why do we
need to bring stuff on chain um there's
a lot of advantages or like a lot of
things that that are interesting about
it what I mentioned in the beginning um
you have something that can potentially
live on forever
um you have the potential to for other
people to extend it in in more
interesting ways because they can deploy
code to basically the same computer that
this game is running on um in terms of
skat ability that's kind of what we're
working on and also a lot of lot of
other people are working on um I think
it's interesting to try to push the um
the you know like try to put more
ambitious things on chain to basically
extend what we can uh do on chain um to
kind of remove those limitations